diff --git a/src/config/generalconf.cpp b/src/config/generalconf.cpp index a95680899e..91bf8c904f 100644 --- a/src/config/generalconf.cpp +++ b/src/config/generalconf.cpp @@ -55,6 +55,7 @@ GeneralConf::GeneralConf(QWidget* parent) initCopyPathAfterSave(); initAntialiasingPinZoom(); initUndoLimit(); + initInsecurePixelate(); #ifdef ENABLE_IMGUR initCopyAndCloseAfterUpload(); initUploadWithoutConfirmation(); @@ -875,6 +876,20 @@ void GeneralConf::initReverseArrow() m_reverseArrow, &QCheckBox::clicked, this, &GeneralConf::setReverseArrow); } +void GeneralConf::initInsecurePixelate() +{ + m_insecurePixelate = new QCheckBox(tr("Insecure Pixelate"), this); + m_insecurePixelate->setToolTip( + tr("Draw the pixelation effect in an insecure but more asethetic way.")); + m_insecurePixelate->setChecked(ConfigHandler().insecurePixelate()); + m_scrollAreaLayout->addWidget(m_insecurePixelate); + + connect(m_insecurePixelate, + &QCheckBox::clicked, + this, + &GeneralConf::setInsecurePixelate); +} + void GeneralConf::setSelGeoHideTime(int v) { ConfigHandler().setValue("showSelectionGeometryHideTime", v); @@ -910,3 +925,8 @@ void GeneralConf::setReverseArrow(bool checked) { ConfigHandler().setReverseArrow(checked); } + +void GeneralConf::setInsecurePixelate(bool checked) +{ + ConfigHandler().setInsecurePixelate(checked); +} \ No newline at end of file diff --git a/src/config/generalconf.h b/src/config/generalconf.h index a384039a47..9bf09b29af 100644 --- a/src/config/generalconf.h +++ b/src/config/generalconf.h @@ -61,6 +61,7 @@ private slots: void setSelGeoHideTime(int v); void setJpegQuality(int v); void setReverseArrow(bool checked); + void setInsecurePixelate(bool checked); private: const QString chooseFolder(const QString& currentPath = ""); @@ -99,6 +100,7 @@ private slots: void initShowSelectionGeometry(); void initJpegQuality(); void initReverseArrow(); + void initInsecurePixelate(); void _updateComponents(bool allowEmptySavePath); @@ -147,4 +149,5 @@ private slots: QSpinBox* m_xywhTimeout; QSpinBox* m_jpegQuality; QCheckBox* m_reverseArrow; + QCheckBox* m_insecurePixelate; }; diff --git a/src/tools/pixelate/pixelatetool.cpp b/src/tools/pixelate/pixelatetool.cpp index 4bbccf7ae8..85ea56a480 100644 --- a/src/tools/pixelate/pixelatetool.cpp +++ b/src/tools/pixelate/pixelatetool.cpp @@ -11,6 +11,7 @@ #include #include +#include "confighandler.h" PixelateTool::PixelateTool(QObject* parent) : AbstractTwoPointTool(parent) {} @@ -60,133 +61,164 @@ CaptureTool* PixelateTool::copy(QObject* parent) */ void PixelateTool::process(QPainter& painter, const QPixmap& pixmap) { + bool useInsecurePixelate = ConfigHandler().insecurePixelate(); + QRect selection = boundingRect().intersected(pixmap.rect()); auto pixelRatio = pixmap.devicePixelRatio(); QRect selectionScaled = QRect(selection.topLeft() * pixelRatio, selection.bottomRight() * pixelRatio); - // calculate the size of the pixelation effect using the tool size - int width = qMax( - 1, static_cast(selection.width() * (0.5 / qMax(1, size() + 1)))); - int height = qMax( - 1, static_cast(selection.height() * (0.5 / qMax(1, size() + 1)))); - - QSize effect_size = QSize(width, height); - - // the PRNG is only used for visual effects and NOT part of the security - // boundary - std::mt19937 prng(42); - - // noise for the sampling process to avoid only sampling from a small - // subset of the fringe - std::normal_distribution sampling_noise(0, 5 * size() + 1); - - // additional noise that will be added on top of the effect to avoid - // generating a monochromatic box when the fringe is monochromatic - std::normal_distribution noise(0, 0.1f); - - QPoint offset_top(0, selectionScaled.topLeft().y() == 0 ? 0 : -1); - QPoint offset_bottom( - 0, - selectionScaled.bottomLeft().y() == pixmap.rect().bottomLeft().y() ? 0 - : 1); - QPoint offset_left(selectionScaled.topLeft().x() == 0 ? 0 : -1, 0); - QPoint offset_right( - selectionScaled.topRight().x() == pixmap.rect().topRight().x() ? 0 : 1, - 0); - - // only values from the fringe will be used to compute the pseudo-pixelation - std::array fringe = { - // top fringe - pixmap - .copy(QRect(selectionScaled.topLeft() + offset_top, - selectionScaled.topRight() + offset_top)) - .toImage(), - // bottom fringe - pixmap - .copy(QRect(selectionScaled.bottomLeft() + offset_bottom, - selectionScaled.bottomRight() + offset_bottom)) - .toImage(), - // left fringe - pixmap - .copy(QRect(selectionScaled.topLeft() + offset_left, - selectionScaled.bottomLeft() + offset_left)) - .toImage(), - // right fringe - pixmap - .copy(QRect(selectionScaled.topRight() + offset_right, - selectionScaled.bottomRight() + offset_right)) - .toImage() - }; - - // Image where the pseudo-pixelation is calculated. - // This will later be scaled to cover the selected area. - QImage pixelated = QImage(effect_size, QImage::Format_RGB32); - - // For every pixel of the effect, we consider four projections - // to the fringe and sample a pixel from there. - // Then a horizontal and vertical interpolation are calculated. - std::array, 4> samples; - - for (int x = 0; x < width; ++x) { - for (int y = 0; y < height; ++y) { - float n = noise(prng); - - // relative horizontal resp. vertical position - float horizontal = x / (float)width; - float vertical = y / (float)height; - - for (int i = 0; i < 4; ++i) { - QColor c = fringe[i].pixel( - std::clamp(static_cast(horizontal * fringe[i].width() + - sampling_noise(prng)), - 0, - fringe[i].width() - 1), - std::clamp(static_cast(vertical * fringe[i].height() + - sampling_noise(prng)), - 0, - fringe[i].height() - 1)); - samples[i][0] = c.redF(); - samples[i][1] = c.greenF(); - samples[i][2] = c.blueF(); - } - - // weights of the horizontal resp. vertical interpolation - float weight_h = (qMin(x, width - x) / width) - - (qMin(y, height - y) / height) + 0.5; - - float weight_v = 1 - weight_h; - - // compute the weighted sum of the vertical and horizontal - // interpolations - std::array rgb = { 0, 0, 0 }; - for (int i = 0; i < 3; ++i) { - float c = - // horizontal interpolation - weight_h * ((1 - horizontal) * samples[2][i] + - horizontal * samples[3][i]) - - // vertical interpolation - + weight_v * ((1 - vertical) * samples[0][i] + - vertical * samples[1][i]) - - // additional noise - + n; - - rgb[i] = static_cast(0xff * c); - rgb[i] = std::clamp(rgb[i], 0, 0xff); + const auto width = + static_cast(selection.width() * (0.5 / qMax(1, size() + 1))); + const auto height = + static_cast(selection.height() * (0.5 / qMax(1, size() + 1))); + const auto effectSize = QSize(qMax(width, 1), qMax(height, 1)); + + if (useInsecurePixelate) { + if (size() <= 1) { + auto* blur = new QGraphicsBlurEffect(this); + blur->setBlurRadius(10); + auto* item = new QGraphicsPixmapItem(pixmap.copy(selectionScaled)); + item->setGraphicsEffect(blur); + + QGraphicsScene scene; + scene.addItem(item); + + scene.render(&painter, selection, QRectF()); + blur->setBlurRadius(12); + // multiple repeat for make blur effect stronger + scene.render(&painter, selection, QRectF()); + + } else { + auto pixmapPixelated = pixmap.copy(selectionScaled); + pixmapPixelated = pixmapPixelated.scaled( + effectSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + pixmapPixelated = + pixmapPixelated.scaled(selection.width(), selection.height()); + painter.drawImage(selection, pixmapPixelated.toImage()); + } + } else { + // the PRNG is only used for visual effects and NOT part of the security + // boundary + std::mt19937 prng(42); + + // noise for the sampling process to avoid only sampling from a small + // subset of the fringe + std::normal_distribution sampling_noise(0, 5 * size() + 1); + + // additional noise that will be added on top of the effect to avoid + // generating a monochromatic box when the fringe is monochromatic + std::normal_distribution noise(0, 0.1f); + + QPoint const offset_top(0, selectionScaled.topLeft().y() == 0 ? 0 : -1); + QPoint const offset_bottom(0, + selectionScaled.bottomLeft().y() == + pixmap.rect().bottomLeft().y() + ? 0 + : 1); + QPoint const offset_left(selectionScaled.topLeft().x() == 0 ? 0 : -1, + 0); + QPoint const offset_right( + selectionScaled.topRight().x() == pixmap.rect().topRight().x() ? 0 + : 1, + 0); + + // only values from the fringe will be used to compute the + // pseudo-pixelation + std::array fringe = { + // top fringe + pixmap + .copy(QRect(selectionScaled.topLeft() + offset_top, + selectionScaled.topRight() + offset_top)) + .toImage(), + // bottom fringe + pixmap + .copy(QRect(selectionScaled.bottomLeft() + offset_bottom, + selectionScaled.bottomRight() + offset_bottom)) + .toImage(), + // left fringe + pixmap + .copy(QRect(selectionScaled.topLeft() + offset_left, + selectionScaled.bottomLeft() + offset_left)) + .toImage(), + // right fringe + pixmap + .copy(QRect(selectionScaled.topRight() + offset_right, + selectionScaled.bottomRight() + offset_right)) + .toImage() + }; + + // Image where the pseudo-pixelation is calculated. + // This will later be scaled to cover the selected area. + QImage pixelated = QImage(effectSize, QImage::Format_RGB32); + + // For every pixel of the effect, we consider four projections + // to the fringe and sample a pixel from there. + // Then a horizontal and vertical interpolation are calculated. + std::array, 4> samples; + + for (int x = 0; x < width; ++x) { + for (int y = 0; y < height; ++y) { + float n = noise(prng); + + // relative horizontal resp. vertical position + float const horizontal = x / (float)width; + float const vertical = y / (float)height; + + for (int i = 0; i < 4; ++i) { + QColor const c = fringe[i].pixel( + std::clamp( + static_cast(horizontal * fringe[i].width() + + sampling_noise(prng)), + 0, + fringe[i].width() - 1), + std::clamp( + static_cast(vertical * fringe[i].height() + + sampling_noise(prng)), + 0, + fringe[i].height() - 1)); + samples[i][0] = c.redF(); + samples[i][1] = c.greenF(); + samples[i][2] = c.blueF(); + } + + // weights of the horizontal resp. vertical interpolation + float const weight_h = (qMin(x, width - x) / width) - + (qMin(y, height - y) / height) + 0.5; + + float const weight_v = 1 - weight_h; + + // compute the weighted sum of the vertical and horizontal + // interpolations + std::array rgb = { 0, 0, 0 }; + for (int i = 0; i < 3; ++i) { + float c = + // horizontal interpolation + weight_h * ((1 - horizontal) * samples[2][i] + + horizontal * samples[3][i]) + + // vertical interpolation + + weight_v * ((1 - vertical) * samples[0][i] + + vertical * samples[1][i]) + + // additional noise + + n; + + rgb[i] = static_cast(0xff * c); + rgb[i] = std::clamp(rgb[i], 0, 0xff); + } + QRgb const value = qRgb(rgb[0], rgb[1], rgb[2]); + pixelated.setPixel(x, y, value); } - QRgb value = qRgb(rgb[0], rgb[1], rgb[2]); - pixelated.setPixel(x, y, value); } - } - pixelated = pixelated.scaled(selection.width(), - selection.height(), - Qt::IgnoreAspectRatio, - Qt::FastTransformation); + pixelated = pixelated.scaled(selection.width(), + selection.height(), + Qt::IgnoreAspectRatio, + Qt::FastTransformation); - painter.drawImage(selection, pixelated); + painter.drawImage(selection, pixelated); + } } void PixelateTool::drawSearchArea(QPainter& painter, const QPixmap& pixmap) diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index e09d2d157e..2117e12fcd 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -136,6 +136,7 @@ static QMap> OPTION("showSelectionGeometryHideTime", LowerBoundedInt ( 0, 3000 )), OPTION("jpegQuality" , BoundedInt ( 0,100,75 )), OPTION("reverseArrow" ,Bool ( false )), + OPTION("insecurePixelate" ,Bool ( false )), }; static QMap> recognizedShortcuts = { diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index f9e6fcf2d3..feb87a299d 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -136,6 +136,7 @@ class ConfigHandler : public QObject CONFIG_GETTER_SETTER(showSelectionGeometry, setShowSelectionGeometry, int) CONFIG_GETTER_SETTER(jpegQuality, setJpegQuality, int) CONFIG_GETTER_SETTER(reverseArrow, setReverseArrow, bool) + CONFIG_GETTER_SETTER(insecurePixelate, setInsecurePixelate, bool) CONFIG_GETTER_SETTER(showSelectionGeometryHideTime, showSelectionGeometryHideTime, int)