@@ -15,10 +15,22 @@ namespace
15
15
void scalePoint (const QPointF& source, QPoint& target, qreal scalingFactor);
16
16
}
17
17
18
+ #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
19
+ #define GET_BOUNDING_RECTANGLE (name ) QRectF name{ 0 , 0 , 400 , 400 }
20
+ #else
21
+ #define GET_BOUNDING_RECTANGLE (name ) QRectF name = boundingRect()
22
+ #endif
23
+
24
+ #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
25
+ #define UPDATE_IMAGE () paint(nullptr )
26
+ #else
27
+ #define UPDATE_IMAGE () update()
28
+ #endif
29
+
18
30
SourceImage::SourceImage (QQuickItem* parent)
19
31
: QQuickPaintedItem()
20
32
, filePath{}
21
- , resultSize{}
33
+ , resultSize{1 , 1 }
22
34
, clipTopLeft{}
23
35
, clipBottomRight{}
24
36
, image{}
@@ -36,14 +48,14 @@ SourceImage::SourceImage(QQuickItem* parent)
36
48
void SourceImage::mousePressEvent (QMouseEvent* theEvent)
37
49
{
38
50
newStartingPoint = { theEvent->localPos ().x (), theEvent->localPos ().y () };
39
- logging::logger () << logging::Level::DEBUG << " MousePress: top left is " << newTopLeft .x () << " , " << newTopLeft .y () << logging::Level::OFF;
51
+ logging::logger () << logging::Level::DEBUG << " MousePress: top left is " << newStartingPoint .x () << " , " << newStartingPoint .y () << logging::Level::OFF;
40
52
}
41
53
42
54
void SourceImage::mouseMoveEvent (QMouseEvent* theEvent)
43
55
{
44
56
if (theEvent->buttons () & Qt::MouseButton::LeftButton)
45
57
{
46
- QRectF bounds = boundingRect ( );
58
+ GET_BOUNDING_RECTANGLE (bounds );
47
59
logging::logger () << logging::Level::DEBUG << " MouseMove: boundingRect " << bounds.width () << " , " << bounds.height () << logging::Level::OFF;
48
60
QPointF tl = { boundedMin (newStartingPoint.x (), theEvent->localPos ().x (), 0 ), boundedMin (newStartingPoint.y (), theEvent->localPos ().y (), 0 ) };
49
61
QPointF br = { boundedMax (newStartingPoint.x (), theEvent->localPos ().x (), bounds.width () - 1 ), boundedMax (newStartingPoint.y (), theEvent->localPos ().y (), bounds.height () - 1 ) };
@@ -53,7 +65,7 @@ void SourceImage::mouseMoveEvent(QMouseEvent* theEvent)
53
65
if (! abort )
54
66
{
55
67
logging::logger () << logging::Level::DEBUG << " MouseMove: top left is " << newTopLeft.x () << " , " << newTopLeft.y () << " , bottom right is " << newBottomRight.x () << " , " << newBottomRight.y () << logging::Level::OFF;
56
- update ();
68
+ UPDATE_IMAGE ();
57
69
}
58
70
}
59
71
}
@@ -66,7 +78,7 @@ void SourceImage::mouseReleaseEvent(QMouseEvent* theEvent)
66
78
}
67
79
else
68
80
{
69
- QRectF bounds = boundingRect ( );
81
+ GET_BOUNDING_RECTANGLE (bounds );
70
82
QPointF tl = { boundedMin (newStartingPoint.x (), theEvent->localPos ().x (), 0 ), boundedMin (newStartingPoint.y (), theEvent->localPos ().y (), 0 ) };
71
83
QPointF br = { boundedMax (newStartingPoint.x (), theEvent->localPos ().x (), bounds.width () - 1 ), boundedMax (newStartingPoint.y (), theEvent->localPos ().y (), bounds.height ()) };
72
84
topLeft = tl;
@@ -77,7 +89,7 @@ void SourceImage::mouseReleaseEvent(QMouseEvent* theEvent)
77
89
newStartingPoint = { -1 , -1 };
78
90
newTopLeft = { -1 , -1 };
79
91
newBottomRight = { -1 , -1 };
80
- update (); // triggers paint(...)
92
+ UPDATE_IMAGE ();
81
93
logging::logger () << logging::Level::NOTE << " Clipping to (" << clipTopLeft.x () << " , " << clipTopLeft.y () << " )/(" << clipBottomRight.x () << " , " << clipBottomRight.y () << " )" << logging::Level::OFF;
82
94
logging::logger () << logging::Level::NOTE << " Based on (" << topLeft.x () << " , " << topLeft.y () << " )/(" << bottomRight.x () << " , " << bottomRight.y () << " )" << logging::Level::OFF;
83
95
}
@@ -89,25 +101,21 @@ void SourceImage::setPath(const QUrl& data)
89
101
const std::string fileQuality{ image.isNull () ? " empty " : " " };
90
102
logging::logger () << logging::Level::NOTE << " Loaded a new " << fileQuality << " file " << data.toString ().toStdString () << logging::Level::OFF;
91
103
topLeft = { 0 , 0 };
92
- #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
93
- QRectF bounds{ 0 , 0 , 400 , 400 };
94
- #else
95
- QRectF bounds = boundingRect ();
96
- #endif
104
+ GET_BOUNDING_RECTANGLE (bounds);
97
105
bottomRight = { bounds.width () - 1 , bounds.height () - 1 };
98
106
newTopLeft = { -1 , -1 };
99
107
newBottomRight = { -1 , -1 };
100
108
clipTopLeft = { 0 , 0 };
101
109
clipBottomRight = { image.width () - 1 , image.height () - 1 };
102
110
logging::logger () << logging::Level::NOTE << " File Size is " << image.width () << " x" << image.height () << logging::Level::OFF;
103
- update (); // triggers paint(...)
111
+ UPDATE_IMAGE ();
104
112
}
105
113
106
114
void SourceImage::setResultWidth (const int & width)
107
115
{
108
116
logging::logger () << logging::Level::NOTE << " new width: " << width << logging::Level::OFF;
109
117
resultSize.setX (width);
110
- update (); // triggers paint(...)
118
+ UPDATE_IMAGE ();
111
119
}
112
120
113
121
void SourceImage::setResultHeight (const int & height)
@@ -118,14 +126,10 @@ void SourceImage::setResultHeight(const int& height)
118
126
}
119
127
120
128
void SourceImage::paint (QPainter* painter) {
121
- #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
122
- QRectF bounds{ 0 , 0 , 400 , 400 };
123
- #else
124
- QRectF bounds = boundingRect ();
125
- #endif
129
+ GET_BOUNDING_RECTANGLE (bounds);
126
130
if (image.isNull ())
127
131
{
128
- painter->fillRect (bounds, Qt::white);
132
+ if ( nullptr != painter) painter->fillRect (bounds, Qt::white);
129
133
return ;
130
134
}
131
135
QImage scaled = image.scaledToWidth (bounds.width ());
@@ -141,38 +145,42 @@ void SourceImage::paint(QPainter* painter) {
141
145
center.setY (0 );
142
146
setHeight (scaled.height ());
143
147
setWidth (scaled.width ());
144
- painter->drawImage (center, scaled);
148
+ if ( nullptr != painter) painter->drawImage (center, scaled);
145
149
146
150
normalizeLocations (scaled.width (), scaled.height ());
147
- painter->setPen (QColor (255 , 0 , 0 ));
148
- painter->drawRect (topLeft.x (), topLeft.y (), bottomRight.x ()-topLeft.x (), bottomRight.y ()-topLeft.y ());
149
- painter->setPen (QColor (0 , 255 , 0 ));
150
- painter->drawRect (newTopLeft.x (), newTopLeft.y (), newBottomRight.x () - newTopLeft.x (), newBottomRight.y () - newTopLeft.y ());
151
-
151
+ if (nullptr != painter)
152
+ {
153
+ painter->setPen (QColor (255 , 0 , 0 ));
154
+ painter->drawRect (topLeft.x (), topLeft.y (), bottomRight.x () - topLeft.x (), bottomRight.y () - topLeft.y ());
155
+ painter->setPen (QColor (0 , 255 , 0 ));
156
+ painter->drawRect (newTopLeft.x (), newTopLeft.y (), newBottomRight.x () - newTopLeft.x (), newBottomRight.y () - newTopLeft.y ());
157
+ }
152
158
newClipping ();
153
159
dataChanged ();
154
160
}
155
161
156
162
QImage SourceImage::data () const
157
163
{
158
- #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
159
- QRectF bounds{ 0 , 0 , 400 , 400 };
160
- #else
161
- QRectF bounds = boundingRect ();
162
- #endif
163
- QImage scaled = image.scaledToWidth (bounds.width ());
164
+ GET_BOUNDING_RECTANGLE (bounds);
164
165
logging::logger () << logging::Level::DEBUG << " BoundingBox (" << bounds.x () << " , " << bounds.y () << " )/(" << bounds.width () << " , " << bounds.height () << " )" << logging::Level::OFF;
166
+ const std::string inputIsValid{ image.isNull () ? " empty " : " " };
167
+
168
+ // scale overall image to bounding box
169
+ QImage scaled = image.scaledToWidth (bounds.width ());
165
170
if (scaled.height () > bounds.height ())
166
171
{
167
172
scaled = image.scaledToHeight (bounds.height ());
168
173
}
169
174
const std::string scaledIsValid{ scaled.isNull () ? " empty " : " " };
170
175
logging::logger () << logging::Level::DEBUG << " Rescaled to " << scaledIsValid << " file with " << scaled.width () << " /" << scaled.height () << " pixels" << logging::Level::OFF;
171
- auto returnValue{ scaled.copy (QRectF (topLeft.x (), topLeft.y (), std::min (bottomRight.x () + 1 , (qreal)scaled.width ()), std::min (bottomRight.y () + 1 , (qreal)scaled.height ())).toRect ()) };
176
+
177
+ // clip to selected area
178
+ auto returnValue{ scaled.copy (QRectF (topLeft.x (), topLeft.y (), std::min (bottomRight.x () - topLeft.x () + 1 , (qreal)scaled.width ()), std::min (bottomRight.y () - topLeft.y () + 1 , (qreal)scaled.height ())).toRect ()) };
172
179
const std::string outputIsValid{ returnValue.isNull () ? " empty " : " " };
173
180
logging::logger () << logging::Level::DEBUG << " Actually clipped to " << outputIsValid << " file with " << returnValue.width () << " /" << returnValue.height () << " pixels" << logging::Level::OFF;
174
- logging::logger () << logging::Level::DEBUG << " Clipping to (" << clipTopLeft.x () << " , " << clipTopLeft.y () << " )/(" << clipBottomRight.x () + 1 << " , " << clipBottomRight.y () + 1 << " )" << logging::Level::OFF;
175
- const std::string inputIsValid{ image.isNull () ? " empty " : " " };
181
+
182
+ // validate and return
183
+ logging::logger () << logging::Level::DEBUG << " Clipping to (" << clipX () << " , " << clipY () << " )/(" << clipWidth () << " , " << clipHeight () << " )" << logging::Level::OFF;
176
184
logging::logger () << logging::Level::DEBUG << " Returning " << outputIsValid << " file copied from " << inputIsValid << " input" << logging::Level::OFF;
177
185
return returnValue;
178
186
}
@@ -208,6 +216,7 @@ QString SourceImage::clippingInfo() const
208
216
209
217
void SourceImage::normalizeLocations (qreal paintedWidth, qreal paintedHeight)
210
218
{
219
+ logging::logger () << logging::Level::DEBUG << " Normalizing..." << logging::Level::OFF;
211
220
double definedAspectRatio = resultSize.y () / resultSize.x ();
212
221
213
222
adjustToAspectRatio (topLeft, bottomRight, definedAspectRatio, paintedWidth, paintedHeight);
@@ -218,6 +227,7 @@ void SourceImage::normalizeLocations(qreal paintedWidth, qreal paintedHeight)
218
227
scalePoint (bottomRight, clipBottomRight, scaling);
219
228
scalePoint (newTopLeft, newClipTopLeft, scaling);
220
229
scalePoint (newBottomRight, newClipBottomRight, scaling);
230
+ logging::logger () << logging::Level::DEBUG << " Done." << logging::Level::OFF;
221
231
}
222
232
223
233
namespace
@@ -306,14 +316,42 @@ namespace
306
316
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
307
317
#include < doctest.h>
308
318
319
+ class SourceImageTester
320
+ {
321
+ public:
322
+ SourceImageTester (SourceImage& imageToTest) : m_imageToTest{ imageToTest } {}
323
+
324
+ inline void mouseDown (const QPointF& pos)
325
+ {
326
+ QMouseEvent mousePressed (QEvent::Type::MouseButtonPress, pos, Qt::MouseButton::LeftButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
327
+ m_imageToTest.mousePressEvent (&mousePressed);
328
+ }
329
+
330
+ inline void mouseMoved (const QPointF& pos)
331
+ {
332
+ QMouseEvent mouseMoved (QEvent::Type::MouseMove, pos, Qt::MouseButton::NoButton, Qt::MouseButton::LeftButton, Qt::KeyboardModifier::NoModifier);
333
+ m_imageToTest.mouseMoveEvent (&mouseMoved);
334
+ }
335
+
336
+ inline void mouseUp (const QPointF& pos, bool canceled = false )
337
+ {
338
+ QMouseEvent mouseReleased (QEvent::Type::MouseButtonRelease, pos, Qt::MouseButton::LeftButton, (canceled ? Qt::MouseButton::RightButton : Qt::MouseButton::NoButton), Qt::KeyboardModifier::NoModifier);
339
+ m_imageToTest.mouseReleaseEvent (&mouseReleased);
340
+ }
341
+ private:
342
+ SourceImage& m_imageToTest;
343
+ };
344
+
309
345
TEST_CASE (" test boundedMin" ) {
310
346
CHECK_EQ (boundedMin (1 , 2 , 3 ), 3 );
311
347
CHECK_EQ (boundedMin (3 , 2 , 1 ), 2 );
312
348
}
349
+
313
350
TEST_CASE (" test boundedMax" ) {
314
351
CHECK_EQ (boundedMax (4 , 3 , 2 ), 2 );
315
352
CHECK_EQ (boundedMax (2 , 3 , 4 ), 3 );
316
353
}
354
+
317
355
TEST_CASE (" test adjustToAspectRatio" ) {
318
356
QPointF topLeft{0 , 0 };
319
357
double targetAspectRatio{ 1 . };
@@ -349,18 +387,22 @@ TEST_CASE("test scalePoint") {
349
387
CHECK_EQ (target.x (), 20 );
350
388
}
351
389
352
- void testFileLoad (const QUrl& in_url, SourceImage& inout_imgObject, const QString& in_expected_clipping_info, const QSize& in_expected_size)
390
+ void fileSizeCheck (const SourceImage& inout_imgObject, const QString& in_expected_clipping_info, const QSize& in_expected_size)
353
391
{
354
- std::cout << " As url " << in_url.url ().toStdString () << std::endl;
355
- inout_imgObject.setPath (in_url);
356
- std::cout << inout_imgObject.clippingInfo ().toStdString () << std::endl;
357
392
REQUIRE_FALSE (inout_imgObject.data ().isNull ());
358
393
std::cout << inout_imgObject.clippingInfo ().toStdString () << std::endl;
359
394
CHECK_EQ (inout_imgObject.clippingInfo (), in_expected_clipping_info);
360
395
auto actualSize = inout_imgObject.data ().size ();
361
396
CHECK_EQ (actualSize.width (), in_expected_size.width ());
362
397
CHECK_EQ (actualSize.height (), in_expected_size.height ());
398
+ }
363
399
400
+ void testFileLoad (const QUrl& in_url, SourceImage& inout_imgObject, const QString& in_expected_clipping_info, const QSize& in_expected_size)
401
+ {
402
+ std::cout << " As url " << in_url.url ().toStdString () << std::endl;
403
+ inout_imgObject.setPath (in_url);
404
+ std::cout << inout_imgObject.clippingInfo ().toStdString () << std::endl;
405
+ fileSizeCheck (inout_imgObject, in_expected_clipping_info, in_expected_size);
364
406
}
365
407
366
408
TEST_CASE (" test initial image load" ) {
@@ -396,4 +438,29 @@ TEST_CASE("test initial image load") {
396
438
testFileLoad (resourceUrl, testImage, QString (" Use 0, 0 (w:400, h:400)" ), expectedSize);
397
439
}
398
440
441
+ TEST_CASE (" test mouse based range selection" )
442
+ {
443
+ SourceImage testImage;
444
+ logging::logger ().setLogLevel (logging::Level::DEBUG);
445
+
446
+ testImage.setResultWidth (10 );
447
+ testImage.setResultHeight (10 );
448
+
449
+ QUrl resourceUrl;
450
+ resourceUrl.setScheme (" file" );
451
+ resourceUrl.setPath (TEST_FILE_NAME);
452
+ testImage.setPath (resourceUrl);
453
+
454
+ SourceImageTester tester{ testImage };
455
+
456
+ // drag rectangle from 10, 10 to 200, 200 computationally
457
+ tester.mouseDown ({ 10 , 10 });
458
+ for (qreal pos = 20 ; pos < 200 ; pos += 10 ) tester.mouseMoved ({ pos, pos });
459
+ tester.mouseUp ({ 200 , 200 });
460
+
461
+ // this mouse action should result in a rectangle with 191x191 displayed pixels starting at 10x10 displayed
462
+ // this rectangle, if resized to match the overall image size of 927x927, scales to a 442x442 rectangle starting at pixel 23x23.
463
+ fileSizeCheck (testImage, QString{" Use 23, 23 (w:442, h:442)" }, QSize{191 , 191 });
464
+ }
465
+
399
466
#endif
0 commit comments