Skip to content

Commit 4f224d9

Browse files
Add option for insecure pixelation. Restor blur when size == 1 and insecure option is enabled (flameshot-org#4082)
1 parent 7ae2be6 commit 4f224d9

File tree

5 files changed

+175
-118
lines changed

5 files changed

+175
-118
lines changed

src/config/generalconf.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ GeneralConf::GeneralConf(QWidget* parent)
5555
initCopyPathAfterSave();
5656
initAntialiasingPinZoom();
5757
initUndoLimit();
58+
initInsecurePixelate();
5859
#ifdef ENABLE_IMGUR
5960
initCopyAndCloseAfterUpload();
6061
initUploadWithoutConfirmation();
@@ -875,6 +876,20 @@ void GeneralConf::initReverseArrow()
875876
m_reverseArrow, &QCheckBox::clicked, this, &GeneralConf::setReverseArrow);
876877
}
877878

879+
void GeneralConf::initInsecurePixelate()
880+
{
881+
m_insecurePixelate = new QCheckBox(tr("Insecure Pixelate"), this);
882+
m_insecurePixelate->setToolTip(
883+
tr("Draw the pixelation effect in an insecure but more asethetic way."));
884+
m_insecurePixelate->setChecked(ConfigHandler().insecurePixelate());
885+
m_scrollAreaLayout->addWidget(m_insecurePixelate);
886+
887+
connect(m_insecurePixelate,
888+
&QCheckBox::clicked,
889+
this,
890+
&GeneralConf::setInsecurePixelate);
891+
}
892+
878893
void GeneralConf::setSelGeoHideTime(int v)
879894
{
880895
ConfigHandler().setValue("showSelectionGeometryHideTime", v);
@@ -910,3 +925,8 @@ void GeneralConf::setReverseArrow(bool checked)
910925
{
911926
ConfigHandler().setReverseArrow(checked);
912927
}
928+
929+
void GeneralConf::setInsecurePixelate(bool checked)
930+
{
931+
ConfigHandler().setInsecurePixelate(checked);
932+
}

src/config/generalconf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private slots:
6161
void setSelGeoHideTime(int v);
6262
void setJpegQuality(int v);
6363
void setReverseArrow(bool checked);
64+
void setInsecurePixelate(bool checked);
6465

6566
private:
6667
const QString chooseFolder(const QString& currentPath = "");
@@ -99,6 +100,7 @@ private slots:
99100
void initShowSelectionGeometry();
100101
void initJpegQuality();
101102
void initReverseArrow();
103+
void initInsecurePixelate();
102104

103105
void _updateComponents(bool allowEmptySavePath);
104106

@@ -147,4 +149,5 @@ private slots:
147149
QSpinBox* m_xywhTimeout;
148150
QSpinBox* m_jpegQuality;
149151
QCheckBox* m_reverseArrow;
152+
QCheckBox* m_insecurePixelate;
150153
};

src/tools/pixelate/pixelatetool.cpp

Lines changed: 150 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <array>
1212
#include <random>
1313

14+
#include "confighandler.h"
1415
PixelateTool::PixelateTool(QObject* parent)
1516
: AbstractTwoPointTool(parent)
1617
{}
@@ -60,133 +61,164 @@ CaptureTool* PixelateTool::copy(QObject* parent)
6061
*/
6162
void PixelateTool::process(QPainter& painter, const QPixmap& pixmap)
6263
{
64+
bool useInsecurePixelate = ConfigHandler().insecurePixelate();
65+
6366
QRect selection = boundingRect().intersected(pixmap.rect());
6467
auto pixelRatio = pixmap.devicePixelRatio();
6568
QRect selectionScaled = QRect(selection.topLeft() * pixelRatio,
6669
selection.bottomRight() * pixelRatio);
6770

68-
// calculate the size of the pixelation effect using the tool size
69-
int width = qMax(
70-
1, static_cast<int>(selection.width() * (0.5 / qMax(1, size() + 1))));
71-
int height = qMax(
72-
1, static_cast<int>(selection.height() * (0.5 / qMax(1, size() + 1))));
73-
74-
QSize effect_size = QSize(width, height);
75-
76-
// the PRNG is only used for visual effects and NOT part of the security
77-
// boundary
78-
std::mt19937 prng(42);
79-
80-
// noise for the sampling process to avoid only sampling from a small
81-
// subset of the fringe
82-
std::normal_distribution<float> sampling_noise(0, 5 * size() + 1);
83-
84-
// additional noise that will be added on top of the effect to avoid
85-
// generating a monochromatic box when the fringe is monochromatic
86-
std::normal_distribution<float> noise(0, 0.1f);
87-
88-
QPoint offset_top(0, selectionScaled.topLeft().y() == 0 ? 0 : -1);
89-
QPoint offset_bottom(
90-
0,
91-
selectionScaled.bottomLeft().y() == pixmap.rect().bottomLeft().y() ? 0
92-
: 1);
93-
QPoint offset_left(selectionScaled.topLeft().x() == 0 ? 0 : -1, 0);
94-
QPoint offset_right(
95-
selectionScaled.topRight().x() == pixmap.rect().topRight().x() ? 0 : 1,
96-
0);
97-
98-
// only values from the fringe will be used to compute the pseudo-pixelation
99-
std::array<QImage, 4> fringe = {
100-
// top fringe
101-
pixmap
102-
.copy(QRect(selectionScaled.topLeft() + offset_top,
103-
selectionScaled.topRight() + offset_top))
104-
.toImage(),
105-
// bottom fringe
106-
pixmap
107-
.copy(QRect(selectionScaled.bottomLeft() + offset_bottom,
108-
selectionScaled.bottomRight() + offset_bottom))
109-
.toImage(),
110-
// left fringe
111-
pixmap
112-
.copy(QRect(selectionScaled.topLeft() + offset_left,
113-
selectionScaled.bottomLeft() + offset_left))
114-
.toImage(),
115-
// right fringe
116-
pixmap
117-
.copy(QRect(selectionScaled.topRight() + offset_right,
118-
selectionScaled.bottomRight() + offset_right))
119-
.toImage()
120-
};
121-
122-
// Image where the pseudo-pixelation is calculated.
123-
// This will later be scaled to cover the selected area.
124-
QImage pixelated = QImage(effect_size, QImage::Format_RGB32);
125-
126-
// For every pixel of the effect, we consider four projections
127-
// to the fringe and sample a pixel from there.
128-
// Then a horizontal and vertical interpolation are calculated.
129-
std::array<std::array<float, 3>, 4> samples;
130-
131-
for (int x = 0; x < width; ++x) {
132-
for (int y = 0; y < height; ++y) {
133-
float n = noise(prng);
134-
135-
// relative horizontal resp. vertical position
136-
float horizontal = x / (float)width;
137-
float vertical = y / (float)height;
138-
139-
for (int i = 0; i < 4; ++i) {
140-
QColor c = fringe[i].pixel(
141-
std::clamp(static_cast<int>(horizontal * fringe[i].width() +
142-
sampling_noise(prng)),
143-
0,
144-
fringe[i].width() - 1),
145-
std::clamp(static_cast<int>(vertical * fringe[i].height() +
146-
sampling_noise(prng)),
147-
0,
148-
fringe[i].height() - 1));
149-
samples[i][0] = c.redF();
150-
samples[i][1] = c.greenF();
151-
samples[i][2] = c.blueF();
152-
}
153-
154-
// weights of the horizontal resp. vertical interpolation
155-
float weight_h = (qMin(x, width - x) / width) -
156-
(qMin(y, height - y) / height) + 0.5;
157-
158-
float weight_v = 1 - weight_h;
159-
160-
// compute the weighted sum of the vertical and horizontal
161-
// interpolations
162-
std::array<int, 3> rgb = { 0, 0, 0 };
163-
for (int i = 0; i < 3; ++i) {
164-
float c =
165-
// horizontal interpolation
166-
weight_h * ((1 - horizontal) * samples[2][i] +
167-
horizontal * samples[3][i])
168-
169-
// vertical interpolation
170-
+ weight_v * ((1 - vertical) * samples[0][i] +
171-
vertical * samples[1][i])
172-
173-
// additional noise
174-
+ n;
175-
176-
rgb[i] = static_cast<int>(0xff * c);
177-
rgb[i] = std::clamp(rgb[i], 0, 0xff);
71+
const auto width =
72+
static_cast<int>(selection.width() * (0.5 / qMax(1, size() + 1)));
73+
const auto height =
74+
static_cast<int>(selection.height() * (0.5 / qMax(1, size() + 1)));
75+
const auto effectSize = QSize(qMax(width, 1), qMax(height, 1));
76+
77+
if (useInsecurePixelate) {
78+
if (size() <= 1) {
79+
auto* blur = new QGraphicsBlurEffect(this);
80+
blur->setBlurRadius(10);
81+
auto* item = new QGraphicsPixmapItem(pixmap.copy(selectionScaled));
82+
item->setGraphicsEffect(blur);
83+
84+
QGraphicsScene scene;
85+
scene.addItem(item);
86+
87+
scene.render(&painter, selection, QRectF());
88+
blur->setBlurRadius(12);
89+
// multiple repeat for make blur effect stronger
90+
scene.render(&painter, selection, QRectF());
91+
92+
} else {
93+
auto pixmapPixelated = pixmap.copy(selectionScaled);
94+
pixmapPixelated = pixmapPixelated.scaled(
95+
effectSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
96+
pixmapPixelated =
97+
pixmapPixelated.scaled(selection.width(), selection.height());
98+
painter.drawImage(selection, pixmapPixelated.toImage());
99+
}
100+
} else {
101+
// the PRNG is only used for visual effects and NOT part of the security
102+
// boundary
103+
std::mt19937 prng(42);
104+
105+
// noise for the sampling process to avoid only sampling from a small
106+
// subset of the fringe
107+
std::normal_distribution<float> sampling_noise(0, 5 * size() + 1);
108+
109+
// additional noise that will be added on top of the effect to avoid
110+
// generating a monochromatic box when the fringe is monochromatic
111+
std::normal_distribution<float> noise(0, 0.1f);
112+
113+
QPoint const offset_top(0, selectionScaled.topLeft().y() == 0 ? 0 : -1);
114+
QPoint const offset_bottom(0,
115+
selectionScaled.bottomLeft().y() ==
116+
pixmap.rect().bottomLeft().y()
117+
? 0
118+
: 1);
119+
QPoint const offset_left(selectionScaled.topLeft().x() == 0 ? 0 : -1,
120+
0);
121+
QPoint const offset_right(
122+
selectionScaled.topRight().x() == pixmap.rect().topRight().x() ? 0
123+
: 1,
124+
0);
125+
126+
// only values from the fringe will be used to compute the
127+
// pseudo-pixelation
128+
std::array<QImage, 4> fringe = {
129+
// top fringe
130+
pixmap
131+
.copy(QRect(selectionScaled.topLeft() + offset_top,
132+
selectionScaled.topRight() + offset_top))
133+
.toImage(),
134+
// bottom fringe
135+
pixmap
136+
.copy(QRect(selectionScaled.bottomLeft() + offset_bottom,
137+
selectionScaled.bottomRight() + offset_bottom))
138+
.toImage(),
139+
// left fringe
140+
pixmap
141+
.copy(QRect(selectionScaled.topLeft() + offset_left,
142+
selectionScaled.bottomLeft() + offset_left))
143+
.toImage(),
144+
// right fringe
145+
pixmap
146+
.copy(QRect(selectionScaled.topRight() + offset_right,
147+
selectionScaled.bottomRight() + offset_right))
148+
.toImage()
149+
};
150+
151+
// Image where the pseudo-pixelation is calculated.
152+
// This will later be scaled to cover the selected area.
153+
QImage pixelated = QImage(effectSize, QImage::Format_RGB32);
154+
155+
// For every pixel of the effect, we consider four projections
156+
// to the fringe and sample a pixel from there.
157+
// Then a horizontal and vertical interpolation are calculated.
158+
std::array<std::array<float, 3>, 4> samples;
159+
160+
for (int x = 0; x < width; ++x) {
161+
for (int y = 0; y < height; ++y) {
162+
float n = noise(prng);
163+
164+
// relative horizontal resp. vertical position
165+
float const horizontal = x / (float)width;
166+
float const vertical = y / (float)height;
167+
168+
for (int i = 0; i < 4; ++i) {
169+
QColor const c = fringe[i].pixel(
170+
std::clamp(
171+
static_cast<int>(horizontal * fringe[i].width() +
172+
sampling_noise(prng)),
173+
0,
174+
fringe[i].width() - 1),
175+
std::clamp(
176+
static_cast<int>(vertical * fringe[i].height() +
177+
sampling_noise(prng)),
178+
0,
179+
fringe[i].height() - 1));
180+
samples[i][0] = c.redF();
181+
samples[i][1] = c.greenF();
182+
samples[i][2] = c.blueF();
183+
}
184+
185+
// weights of the horizontal resp. vertical interpolation
186+
float const weight_h = (qMin(x, width - x) / width) -
187+
(qMin(y, height - y) / height) + 0.5;
188+
189+
float const weight_v = 1 - weight_h;
190+
191+
// compute the weighted sum of the vertical and horizontal
192+
// interpolations
193+
std::array<int, 3> rgb = { 0, 0, 0 };
194+
for (int i = 0; i < 3; ++i) {
195+
float c =
196+
// horizontal interpolation
197+
weight_h * ((1 - horizontal) * samples[2][i] +
198+
horizontal * samples[3][i])
199+
200+
// vertical interpolation
201+
+ weight_v * ((1 - vertical) * samples[0][i] +
202+
vertical * samples[1][i])
203+
204+
// additional noise
205+
+ n;
206+
207+
rgb[i] = static_cast<int>(0xff * c);
208+
rgb[i] = std::clamp(rgb[i], 0, 0xff);
209+
}
210+
QRgb const value = qRgb(rgb[0], rgb[1], rgb[2]);
211+
pixelated.setPixel(x, y, value);
178212
}
179-
QRgb value = qRgb(rgb[0], rgb[1], rgb[2]);
180-
pixelated.setPixel(x, y, value);
181213
}
182-
}
183214

184-
pixelated = pixelated.scaled(selection.width(),
185-
selection.height(),
186-
Qt::IgnoreAspectRatio,
187-
Qt::FastTransformation);
215+
pixelated = pixelated.scaled(selection.width(),
216+
selection.height(),
217+
Qt::IgnoreAspectRatio,
218+
Qt::FastTransformation);
188219

189-
painter.drawImage(selection, pixelated);
220+
painter.drawImage(selection, pixelated);
221+
}
190222
}
191223

192224
void PixelateTool::drawSearchArea(QPainter& painter, const QPixmap& pixmap)

src/utils/confighandler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ static QMap<class QString, QSharedPointer<ValueHandler>>
136136
OPTION("showSelectionGeometryHideTime", LowerBoundedInt ( 0, 3000 )),
137137
OPTION("jpegQuality" , BoundedInt ( 0,100,75 )),
138138
OPTION("reverseArrow" ,Bool ( false )),
139+
OPTION("insecurePixelate" ,Bool ( false )),
139140
};
140141

141142
static QMap<QString, QSharedPointer<KeySequence>> recognizedShortcuts = {

src/utils/confighandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class ConfigHandler : public QObject
136136
CONFIG_GETTER_SETTER(showSelectionGeometry, setShowSelectionGeometry, int)
137137
CONFIG_GETTER_SETTER(jpegQuality, setJpegQuality, int)
138138
CONFIG_GETTER_SETTER(reverseArrow, setReverseArrow, bool)
139+
CONFIG_GETTER_SETTER(insecurePixelate, setInsecurePixelate, bool)
139140
CONFIG_GETTER_SETTER(showSelectionGeometryHideTime,
140141
showSelectionGeometryHideTime,
141142
int)

0 commit comments

Comments
 (0)