Commit 4a3cb514 authored by Méven Car's avatar Méven Car Committed by Nate Graham
Browse files

Support Wayland rectangular selection HiDpi

When the drawn rectangle is on a single screen, the output image uses the screen native resolution.
When the drawn rectangle is on screens with multiple scale factor, the output image is upscaled to correspond to the screen with the hightest scale factor to keep the image undistorted.

Adds also a "scaled" full screen mode, to capture screens content downscaled to screens virtualsize allowing for undistorted content albeit loosing details.

Requires Plasma 5.19.80+

BUG: 409762
BUG: 420863
FIXED-IN: 20.12
parent b79ac774
......@@ -63,22 +63,30 @@ KSWidget::KSWidget(Platform::GrabModes theGrabModes, QWidget *parent)
// the capture mode options first
mCaptureModeLabel = new QLabel(i18n("<b>Capture Mode</b>"), this);
mCaptureArea = new QComboBox(this);
if (theGrabModes.testFlag(Platform::GrabMode::AllScreens)) {
QString lFullScreenLabel = QApplication::screens().count() == 1
? i18n("Full Screen")
: i18n("Full Screen (All Monitors)");
if (theGrabModes.testFlag(Platform::GrabMode::AllScreens)) {
mCaptureArea->insertItem(0, lFullScreenLabel, Spectacle::CaptureMode::AllScreens);
mCaptureArea->insertItem(1, i18n("Rectangular Region"), Spectacle::CaptureMode::RectangularRegion);
}
if (theGrabModes.testFlag(Platform::GrabMode::AllScreensScaled) && QApplication::screens().count() > 1) {
QString lFullScreenLabel = i18n("Full Screen (All Monitors, scaled)");
mCaptureArea->insertItem(1, lFullScreenLabel, Spectacle::CaptureMode::AllScreensScaled);
}
if (theGrabModes.testFlag(Platform::GrabMode::PerScreenImageNative)) {
mCaptureArea->insertItem(2, i18n("Rectangular Region"), Spectacle::CaptureMode::RectangularRegion);
}
if (theGrabModes.testFlag(Platform::GrabMode::CurrentScreen)) {
mCaptureArea->insertItem(2, i18n("Current Screen"), Spectacle::CaptureMode::CurrentScreen);
mCaptureArea->insertItem(3, i18n("Current Screen"), Spectacle::CaptureMode::CurrentScreen);
}
if (theGrabModes.testFlag(Platform::GrabMode::ActiveWindow)) {
mCaptureArea->insertItem(3, i18n("Active Window"), Spectacle::CaptureMode::ActiveWindow);
mCaptureArea->insertItem(4, i18n("Active Window"), Spectacle::CaptureMode::ActiveWindow);
}
if (theGrabModes.testFlag(Platform::GrabMode::WindowUnderCursor)) {
mCaptureArea->insertItem(4, i18n("Window Under Cursor"), Spectacle::CaptureMode::WindowUnderCursor);
mCaptureArea->insertItem(5, i18n("Window Under Cursor"), Spectacle::CaptureMode::WindowUnderCursor);
}
if (theGrabModes.testFlag(Platform::GrabMode::TransientWithParent)) {
mTransientWithParentAvailable = true;
......@@ -225,8 +233,7 @@ void KSWidget::lockOnClickEnabled()
void KSWidget::lockOnClickDisabled()
{
mCaptureOnClick->setCheckState(Qt::Unchecked);
mCaptureOnClick->setEnabled(false);
mCaptureOnClick->hide();
mDelayMsec->setEnabled(true);
}
......@@ -271,6 +278,7 @@ void KSWidget::captureModeChanged(int theIndex)
mCaptureTransientOnly->setEnabled(false);
break;
case Spectacle::CaptureMode::AllScreens:
case Spectacle::CaptureMode::AllScreensScaled:
case Spectacle::CaptureMode::CurrentScreen:
case Spectacle::CaptureMode::RectangularRegion:
mWindowDecorations->setEnabled(false);
......
......@@ -24,6 +24,8 @@
#include <QObject>
#include <QFlags>
#include "QuickEditor/ComparableQPoint.h"
class Platform: public QObject
{
Q_OBJECT
......@@ -36,7 +38,9 @@ class Platform: public QObject
CurrentScreen = 0x02,
ActiveWindow = 0x04,
WindowUnderCursor = 0x08,
TransientWithParent = 0x10
TransientWithParent = 0x10,
AllScreensScaled = 0x20,
PerScreenImageNative= 0x40,
};
using GrabModes = QFlags<GrabMode>;
Q_FLAG(GrabModes)
......@@ -62,6 +66,8 @@ class Platform: public QObject
Q_SIGNALS:
void newScreenshotTaken(const QPixmap &thePixmap);
void newScreensScreenshotTaken(const QVector<QImage> &images);
void newScreenshotFailed();
void windowTitleChanged(const QString &theWindowTitle);
};
......
......@@ -30,6 +30,8 @@
#include <QDBusUnixFileDescriptor>
#include <QDBusPendingCall>
#include <QFutureWatcher>
#include <QScreen>
#include <QGuiApplication>
#include <array>
......@@ -73,6 +75,30 @@ static QImage readImage(int thePipeFd)
return lImage;
}
static QVector<QImage> readImages(int thePipeFd)
{
QByteArray lContent;
if (readData(thePipeFd, lContent) != 0) {
close(thePipeFd);
return QVector<QImage>();
}
close(thePipeFd);
QDataStream lDataStream(lContent);
lDataStream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
QImage lImage;
QVector<QImage> imgs;
while (!lDataStream.atEnd()){
lDataStream >> lImage;
if (!lImage.isNull()) {
imgs << lImage;
}
}
return imgs;
}
/* -- General Plumbing ------------------------------------------------------------------------- */
PlatformKWinWayland::PlatformKWinWayland(QObject *parent) :
......@@ -84,15 +110,6 @@ QString PlatformKWinWayland::platformName() const
return QStringLiteral("KWinWayland");
}
Platform::GrabModes PlatformKWinWayland::supportedGrabModes() const
{
Platform::GrabModes lSupportedModes({ GrabMode::AllScreens, GrabMode::WindowUnderCursor });
if (QApplication::screens().count() > 1) {
lSupportedModes |= Platform::GrabMode::CurrentScreen;
}
return lSupportedModes;
}
static std::array<int, 3> s_plasmaVersion = {-1, -1, -1};
std::array<int, 3> findPlasmaMinorVersion () {
......@@ -138,22 +155,77 @@ std::array<int, 3> findPlasmaMinorVersion () {
return s_plasmaVersion;
}
Platform::GrabModes PlatformKWinWayland::supportedGrabModes() const
{
Platform::GrabModes lSupportedModes({ Platform::GrabMode::AllScreens, GrabMode::WindowUnderCursor });
QList<QScreen *> screens = QApplication::screens();
// TODO remove sometime after Plasma 5.21 is released
// We can handle rectangular selection one one screen not scale factor
// on Plasma < 5.21
if (screenshotScreensAvailable() || (screens.count() == 1 && screens.first()->devicePixelRatio() == 1)) {
lSupportedModes |= Platform::GrabMode::PerScreenImageNative;
}
// TODO remove sometime after Plasma 5.20 is released
auto plasmaVersion = findPlasmaMinorVersion();
if (plasmaVersion.at(0) != -1 && (plasmaVersion.at(0) != 5 || (plasmaVersion.at(1) >= 20))) {
lSupportedModes |= Platform::GrabMode::AllScreensScaled;
}
if (screens.count() > 1) {
lSupportedModes |= Platform::GrabMode::CurrentScreen;
}
return lSupportedModes;
}
bool PlatformKWinWayland::screenshotScreensAvailable() const
{
// TODO remove sometime after Plasma 5.21 is released
auto plasmaVersion = findPlasmaMinorVersion();
// Screenshot screenshotScreens dbus interface requires Plasma 5.21
if (plasmaVersion.at(0) != -1 && (plasmaVersion.at(0) != 5 || (plasmaVersion.at(1) >= 21 || (plasmaVersion.at(1) == 20 && plasmaVersion.at(2) >= 80)))) {
return true;
} else {
return false;
}
}
Platform::ShutterModes PlatformKWinWayland::supportedShutterModes() const
{
// TODO remove sometime after Plasma 5.20 is released
auto plasmaVersion = findPlasmaMinorVersion();
if (plasmaVersion.at(0) != -1 && (plasmaVersion.at(0) != 5 || (plasmaVersion.at(1) >= 20 || (plasmaVersion.at(1) == 19 && plasmaVersion.at(2) >= 80)))) {
return { ShutterMode::Immediate | ShutterMode::OnClick };
if (plasmaVersion.at(0) != -1 && (plasmaVersion.at(0) != 5 || (plasmaVersion.at(1) >= 20))) {
return { ShutterMode::Immediate };
} else {
return { ShutterMode::OnClick };
}
}
void PlatformKWinWayland::doGrab(ShutterMode theShutterMode, GrabMode theGrabMode, bool theIncludePointer, bool theIncludeDecorations)
void PlatformKWinWayland::doGrab(ShutterMode /* theShutterMode */, GrabMode theGrabMode, bool theIncludePointer, bool theIncludeDecorations)
{
switch(theGrabMode) {
case GrabMode::AllScreens: {
doGrabHelper(QStringLiteral("screenshotFullscreen"), theIncludePointer);
case GrabMode::AllScreens:
doGrabHelper(QStringLiteral("screenshotFullscreen"), theIncludePointer, true);
return;
case GrabMode::AllScreensScaled:
doGrabHelper(QStringLiteral("screenshotFullscreen"), theIncludePointer, false);
return;
case GrabMode::PerScreenImageNative:
{
QList<QScreen *> screens = QGuiApplication::screens();
QStringList screenNames;
for (const auto screen : screens) {
screenNames << screen->name();
}
if (screenshotScreensAvailable()) {
doGrabImagesHelper(QStringLiteral("screenshotScreens"), screenNames, theIncludePointer, true);
} else {
// TODO remove sometime after Plasma 5.21 is released
// Use the dbus call screenshotFullscreen to get a single screen screenshot and treat it as a list of images
doGrabImagesHelper(QStringLiteral("screenshotFullscreen"), theIncludePointer, true);
}
return;
}
case GrabMode::CurrentScreen: {
......@@ -185,18 +257,43 @@ void PlatformKWinWayland::startReadImage(int theReadPipe)
[lWatcher, this] () {
lWatcher->deleteLater();
const QImage lImage = lWatcher->result();
emit newScreenshotTaken(QPixmap::fromImage(lImage));
if (lImage.isNull()) {
newScreenshotFailed();
} else {
newScreenshotTaken(QPixmap::fromImage(lImage));
}
}
);
lWatcher->setFuture(QtConcurrent::run(readImage, theReadPipe));
}
template <typename ArgType>
void PlatformKWinWayland::callDBus(const QString &theGrabMethod, ArgType theArgument, int theWriteFile)
void PlatformKWinWayland::startReadImages(int theReadPipe)
{
auto lWatcher = new QFutureWatcher<QVector<QImage>>(this);
QObject::connect(lWatcher, &QFutureWatcher<QVector<QImage>>::finished, this,
[lWatcher, this] () {
lWatcher->deleteLater();
auto result = lWatcher->result();
if (result.isEmpty()) {
newScreenshotFailed();
} else {
newScreensScreenshotTaken(result);
}
}
);
lWatcher->setFuture(QtConcurrent::run(readImages, theReadPipe));
}
template <typename ... ArgType>
void PlatformKWinWayland::callDBus(const QString &theGrabMethod, int theWriteFile, ArgType ... arguments)
{
QDBusInterface lInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot"));
QDBusPendingCall pcall = lInterface.asyncCall(theGrabMethod, QVariant::fromValue(QDBusUnixFileDescriptor(theWriteFile)), theArgument);
QDBusPendingCall pcall = lInterface.asyncCall(theGrabMethod, QVariant::fromValue(QDBusUnixFileDescriptor(theWriteFile)), arguments...);
checkDbusPendingCall(pcall);
}
void PlatformKWinWayland::checkDbusPendingCall(QDBusPendingCall pcall)
{
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
this, [this](QDBusPendingCallWatcher* watcher) {
......@@ -209,8 +306,8 @@ void PlatformKWinWayland::callDBus(const QString &theGrabMethod, ArgType theArgu
});
}
template <typename ArgType>
void PlatformKWinWayland::doGrabHelper(const QString &theGrabMethod, ArgType theArgument)
template <typename ... ArgType>
void PlatformKWinWayland::doGrabHelper(const QString &theGrabMethod, ArgType ... arguments)
{
int lPipeFds[2];
if (pipe2(lPipeFds, O_CLOEXEC|O_NONBLOCK) != 0) {
......@@ -218,8 +315,23 @@ void PlatformKWinWayland::doGrabHelper(const QString &theGrabMethod, ArgType the
return;
}
callDBus(theGrabMethod, theArgument, lPipeFds[1]);
callDBus(theGrabMethod, lPipeFds[1], arguments...);
startReadImage(lPipeFds[0]);
close(lPipeFds[1]);
}
template <typename ... ArgType>
void PlatformKWinWayland::doGrabImagesHelper(const QString &theGrabMethod, ArgType ... arguments)
{
int lPipeFds[2];
if (pipe2(lPipeFds, O_CLOEXEC|O_NONBLOCK) != 0) {
emit newScreenshotFailed();
return;
}
callDBus(theGrabMethod, lPipeFds[1], arguments...);
startReadImages(lPipeFds[0]);
close(lPipeFds[1]);
}
......@@ -23,6 +23,8 @@
#include "Platform.h"
class QDBusPendingCall;
class PlatformKWinWayland final: public Platform
{
Q_OBJECT
......@@ -43,6 +45,13 @@ class PlatformKWinWayland final: public Platform
private:
void startReadImage(int theReadPipe);
template <typename ArgType> void doGrabHelper(const QString &theGrabMethod, ArgType theArgument);
template <typename ArgType> void callDBus(const QString &theGrabMethod, ArgType theArgument, int theWriteFile);
void startReadImages(int theReadPipe);
void checkDbusPendingCall(QDBusPendingCall pcall);
bool screenshotScreensAvailable() const;
template <typename ... ArgType> void callDBus(const QString &theGrabMethod, int theWriteFile, ArgType ... arguments);
template <typename ... ArgType> void doGrabHelper(const QString &theGrabMethod, ArgType ... arguments);
template <typename ... ArgType> void doGrabImagesHelper(const QString &theGrabMethod, ArgType ... arguments);
};
......@@ -36,7 +36,7 @@ QString PlatformNull::platformName() const
Platform::GrabModes PlatformNull::supportedGrabModes() const
{
return { GrabMode::AllScreens | GrabMode::CurrentScreen | GrabMode::ActiveWindow | GrabMode::WindowUnderCursor | GrabMode::TransientWithParent };
return { GrabMode::AllScreens | GrabMode::CurrentScreen | GrabMode::ActiveWindow | GrabMode::WindowUnderCursor | GrabMode::TransientWithParent | GrabMode::AllScreensScaled };
}
Platform::ShutterModes PlatformNull::supportedShutterModes() const
......
......@@ -151,7 +151,7 @@ QString PlatformXcb::platformName() const
Platform::GrabModes PlatformXcb::supportedGrabModes() const
{
Platform::GrabModes lSupportedModes({ GrabMode::AllScreens, GrabMode::ActiveWindow, GrabMode::WindowUnderCursor, GrabMode::TransientWithParent });
Platform::GrabModes lSupportedModes({ GrabMode::AllScreens, GrabMode::ActiveWindow, GrabMode::WindowUnderCursor, GrabMode::TransientWithParent, GrabMode::PerScreenImageNative });
if (QApplication::screens().count() > 1) {
lSupportedModes |= Platform::GrabMode::CurrentScreen;
}
......@@ -689,8 +689,22 @@ void PlatformXcb::doGrabNow(GrabMode theGrabMode, bool theIncludePointer, bool t
{
switch(theGrabMode) {
case GrabMode::AllScreens:
case GrabMode::AllScreensScaled:
grabAllScreens(theIncludePointer);
break;
case GrabMode::PerScreenImageNative:{
auto lPixmap = getToplevelPixmap(QRect(), theIncludePointer);
// break thePixmap into list of images
const auto screens = QGuiApplication::screens();
QVector<QImage> images;
for (const auto screen: screens) {
QRect geom = screen->geometry();
geom.setSize(screen->size() * screen->devicePixelRatio());
images << lPixmap.copy(geom).toImage();
}
emit newScreensScreenshotTaken(images);
break;
}
case GrabMode::CurrentScreen:
grabCurrentScreen(theIncludePointer);
break;
......
/*
* Copyright (C) 2020 Méven Car <meven.car@enioka.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QPoint>
#ifndef COMPARABLEQPOINT_H
#define COMPARABLEQPOINT_H
class ComparableQPoint : public QPoint
{
public:
ComparableQPoint(const QPoint &point): QPoint(point.x(), point.y())
{}
ComparableQPoint(): QPoint()
{}
// utility class that allows using QMap to sort its keys when they are QPoint
bool operator<(const ComparableQPoint &other) const {
return x() < other.x() || y() < other.y();
}
};
#endif // COMPARABLEQPOINT_H
......@@ -55,7 +55,7 @@ const int QuickEditor::magZoom = 5;
const int QuickEditor::magPixels = 16;
const int QuickEditor::magOffset = 32;
QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell *plasmashell, QWidget *parent) :
QuickEditor::QuickEditor(const QMap<ComparableQPoint, QImage> &images, KWayland::Client::PlasmaShell *plasmashell, QWidget *parent) :
QWidget(parent),
mMaskColor(QColor::fromRgbF(0, 0, 0, 0.15)),
mStrokeColor(palette().highlight().color()),
......@@ -72,7 +72,7 @@ QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell
mBottomHelpTextFont(font()),
mBottomHelpGridLeftWidth(0),
mMouseDragState(MouseState::None),
mPixmap(thePixmap),
mImages(images),
mMagnifierAllowed(false),
mShowMagnifier(Settings::showMagnifier()),
mToggleMagnifier(false),
......@@ -91,8 +91,36 @@ QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell
setAttribute(Qt::WA_StaticContents);
setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup | Qt::WindowStaysOnTopHint);
dprI = 1.0 / devicePixelRatioF();
setGeometry(0, 0, static_cast<int>(mPixmap.width() * dprI), static_cast<int>(mPixmap.height() * dprI));
devicePixelRatio = plasmashell ? 1.0 : devicePixelRatioF();
devicePixelRatioI = 1.0 / devicePixelRatio;
int width = 0, height = 0;
for (auto it = mImages.constBegin(); it != mImages.constEnd(); it ++) {
width = qMax(width, it.value().width() + it.key().x());
height = qMax(height, it.value().height() + it.key().y());
}
mPixmap = QPixmap(width, height);
mScreenRegion = QRegion();
const QList<QScreen*> screens = QGuiApplication::screens();
QMap<ComparableQPoint, QPair<qreal, QSize>> input;
for (auto it = mImages.begin(); it != mImages.end(); ++it) {
const auto pos = it.key();
const QImage &screenImage = it.value();
auto item = std::find_if(screens.constBegin(), screens.constEnd(),
[pos] (const QScreen* screen){
return screen->geometry().topLeft() == pos;
});
const QScreen* screen = *item;
input.insert(pos, QPair<qreal, QSize>(screenImage.width() / static_cast<qreal>(screen->size().width()), screenImage.size()));
}
const auto pointsTranslationMap = computeCoordinatesAfterScaling(input);
QPainter painter(&mPixmap);
for (auto it = mImages.constBegin(); it != mImages.constEnd(); it ++) {
painter.drawImage(pointsTranslationMap.value(it.key()), it.value());
}
painter.end();
if (KWindowSystem::isPlatformX11()) {
// Even though we want the quick editor window to be placed at (0, 0) in the native
......@@ -116,6 +144,9 @@ QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell
};
xcb_configure_window(QX11Info::connection(), winId(), mask, values);
resize(width, height);
} else {
setGeometry(0, 0, width, height);
}
// TODO This is a hack until a better interface is available
......@@ -134,11 +165,11 @@ QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell
auto savedRect = Settings::cropRegion();
QRect cropRegion = QRect(savedRect[0], savedRect[1], savedRect[2], savedRect[3]);
if (!cropRegion.isEmpty()) {
mSelection = QRectF(
cropRegion.x() * dprI,
cropRegion.y() * dprI,
cropRegion.width() * dprI,
cropRegion.height() * dprI
mSelection = QRect(
cropRegion.x() * devicePixelRatioI,
cropRegion.y() * devicePixelRatioI,
cropRegion.width() * devicePixelRatioI,
cropRegion.height() * devicePixelRatioI
).intersected(rect());
}
setMouseCursor(QCursor::pos());
......@@ -163,21 +194,82 @@ QuickEditor::QuickEditor(const QPixmap &thePixmap, KWayland::Client::PlasmaShell
}
layoutBottomHelpText();
preparePaint();
update();
}
void QuickEditor::acceptSelection()
{
if (!mSelection.isEmpty()) {
const qreal dpr = devicePixelRatioF();
QRect scaledCropRegion = QRect(
qRound(mSelection.x() * dpr),
qRound(mSelection.y() * dpr),
qRound(mSelection.width() * dpr),
qRound(mSelection.height() * dpr)
qRound(mSelection.x() * devicePixelRatio),
qRound(mSelection.y() * devicePixelRatio),
qRound(mSelection.width() * devicePixelRatio),
qRound(mSelection.height() * devicePixelRatio)
);
Settings::setCropRegion({scaledCropRegion.x(), scaledCropRegion.y(), scaledCropRegion.width(), scaledCropRegion.height()});
if (KWindowSystem::isPlatformX11()) {
emit grabDone(mPixmap.copy(scaledCropRegion));
} else {
// Wayland case
qreal maxDpr = 1.0;
for (const QScreen *screen: QGuiApplication::screens()) {
if (screen->devicePixelRatio() > maxDpr) {
maxDpr = screen->devicePixelRatio();
}
}
QPixmap output(mSelection.size() * maxDpr);
QPainter painter(&output);
QRect intersected;
QPixmap screenOutput;
for (auto it = mRectToDpr.constBegin(); it != mRectToDpr.constEnd(); ++it)
{
const auto screenRect = (*it).first;
if (mSelection.intersects(screenRect)) {
const auto &pos = screenRect.topLeft();
const qreal &dpr = (*it).second;
intersected = screenRect.intersected(mSelection);
// converts to screen size & position
QRect pixelOnScreenIntersected;
pixelOnScreenIntersected.moveTopLeft((intersected.topLeft() - pos) * dpr);
pixelOnScreenIntersected.setWidth(intersected.width() * dpr);
pixelOnScreenIntersected.setHeight(intersected.height() * dpr);
screenOutput = QPixmap::fromImage(mImages.value(pos).copy(pixelOnScreenIntersected));
if (intersected.size() == mSelection.size()) {
painter.end();
// short path when single screen
// keep native screen resolution
emit grabDone(screenOutput);
return;
} else {
// upscale the image according to max screen dpr, to keep the image not distorted
const auto dprI = maxDpr / dpr;
QBrush brush(screenOutput);
brush.setTransform(QTransform().scale(dprI, dprI));
intersected.moveTopLeft((intersected.topLeft() - mSelection.topLeft()) * maxDpr);
intersected.setSize(intersected.size() * maxDpr);
painter.setBrushOrigin(intersected.topLeft());
painter.fillRect(intersected, brush);
}
}
}
painter.end();
emit grabDone(output);
}
}
}
......@@ -202,12 +294,12 @@ void QuickEditor::keyPressEvent(QKeyEvent* event)
break;
}
const qreal step = (shiftPressed ? 1 : magnifierLargeStep);
const int newPos = boundsUp(qRound(mSelection.top() * devicePixelRatioF() - step), false);
const int newPos = boundsUp(qRound(mSelection.top() * devicePixelRatio - step), false);
if (modifiers & Qt::AltModifier) {
mSelection.setBottom(dprI * newPos + mSelection.height());
mSelection.setBottom(devicePixelRatioI * newPos + mSelection.height());
mSelection = mSelection.normalized();
} else {
mSelection.moveTop(dprI * newPos);
mSelection.moveTop(devicePixelRatioI * newPos);
}
update();