1
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 // TODO: Color Similarity is obviously useful in Autocrop but it isn't
29 // obvious as to how to implement it. The current heuristic,
30 // for each side, chooses an arbitrary reference color for which
31 // all other candidate pixels in that side are tested against
32 // for similarity. But if the reference color happens to be at
33 // one extreme of the range of colors in that side, then pixels
34 // at the other extreme would not be deemed similar enough. The
35 // key is to find the median color as the reference but how do
36 // you do this if you don't know which pixels to sample in the first
37 // place (that's what you're trying to find)? Chicken and egg situation.
38 //
39 // The other heuristic that is in doubt is the use of the average
40 // color in determining the similarity of sides (it is possible
41 // to get vastly differently colors in both sides yet they will be
42 // considered similar).
43
44 #define DEBUG_KP_TOOL_AUTO_CROP 0
45
46
47 #include "kpTransformAutoCrop.h"
48
49 #include "layers/selections/image/kpAbstractImageSelection.h"
50 #include "widgets/toolbars/kpColorToolBar.h"
51 #include "environments/commands/kpCommandEnvironment.h"
52 #include "commands/kpCommandHistory.h"
53 #include "document/kpDocument.h"
54 #include "mainWindow/kpMainWindow.h"
55 #include "imagelib/kpPainter.h"
56 #include "pixmapfx/kpPixmapFX.h"
57 #include "layers/selections/image/kpRectangularImageSelection.h"
58 #include "generic/kpSetOverrideCursorSaver.h"
59 #include "tools/kpTool.h"
60 #include "views/manager/kpViewManager.h"
61
62 #include "kpLogCategories.h"
63 #include <KMessageBox>
64 #include <KLocalizedString>
65
66 #include <QImage>
67
68 //---------------------------------------------------------------------
69
70 class kpTransformAutoCropBorder
71 {
72 public:
73 // WARNING: Only call the <ctor> with imagePtr = 0 if you are going to use
74 // operator= to fill it in with a valid imagePtr immediately
75 // afterwards.
76 kpTransformAutoCropBorder (const kpImage *imagePtr = nullptr, int processedColorSimilarity = 0);
77
78 kpCommandSize::SizeType size () const;
79
80 const kpImage *image () const;
81 int processedColorSimilarity () const;
82 QRect rect () const;
83 int left () const;
84 int right () const;
85 int top () const;
86 int bottom () const;
87 kpColor referenceColor () const;
88 kpColor averageColor () const;
89 bool isSingleColor () const;
90
91 // (returns true on success (even if no rect) or false on error)
92 bool calculate (int isX, int dir);
93
94 bool fillsEntireImage () const;
95 bool exists () const;
96 void invalidate ();
97
98 private:
99 const kpImage *m_imagePtr;
100 int m_processedColorSimilarity;
101
102 QRect m_rect;
103 kpColor m_referenceColor;
104 int m_redSum, m_greenSum, m_blueSum;
105 bool m_isSingleColor;
106 };
107
kpTransformAutoCropBorder(const kpImage * imagePtr,int processedColorSimilarity)108 kpTransformAutoCropBorder::kpTransformAutoCropBorder (const kpImage *imagePtr,
109 int processedColorSimilarity)
110 : m_imagePtr (imagePtr),
111 m_processedColorSimilarity (processedColorSimilarity)
112 {
113 invalidate ();
114 }
115
116
117 // public
size() const118 kpCommandSize::SizeType kpTransformAutoCropBorder::size () const
119 {
120 return sizeof (kpTransformAutoCropBorder);
121 }
122
123
124 // public
image() const125 const kpImage *kpTransformAutoCropBorder::image () const
126 {
127 return m_imagePtr;
128 }
129
130 // public
processedColorSimilarity() const131 int kpTransformAutoCropBorder::processedColorSimilarity () const
132 {
133 return m_processedColorSimilarity;
134 }
135
136 // public
rect() const137 QRect kpTransformAutoCropBorder::rect () const
138 {
139 return m_rect;
140 }
141
142 // public
left() const143 int kpTransformAutoCropBorder::left () const
144 {
145 return m_rect.left ();
146 }
147
148 // public
right() const149 int kpTransformAutoCropBorder::right () const
150 {
151 return m_rect.right ();
152 }
153
154 // public
top() const155 int kpTransformAutoCropBorder::top () const
156 {
157 return m_rect.top ();
158 }
159
160 // public
bottom() const161 int kpTransformAutoCropBorder::bottom () const
162 {
163 return m_rect.bottom ();
164 }
165
166 // public
referenceColor() const167 kpColor kpTransformAutoCropBorder::referenceColor () const
168 {
169 return m_referenceColor;
170 }
171
172 // public
averageColor() const173 kpColor kpTransformAutoCropBorder::averageColor () const
174 {
175 if (!m_rect.isValid ())
176 return kpColor::Invalid;
177
178 if (m_referenceColor.isTransparent ())
179 return kpColor::Transparent;
180
181 if (m_processedColorSimilarity == 0)
182 return m_referenceColor;
183
184 int numPixels = (m_rect.width () * m_rect.height ());
185 Q_ASSERT (numPixels > 0);
186
187 return kpColor (m_redSum / numPixels, m_greenSum / numPixels, m_blueSum / numPixels);
188
189 }
190
191 //---------------------------------------------------------------------
192
isSingleColor() const193 bool kpTransformAutoCropBorder::isSingleColor () const
194 {
195 return m_isSingleColor;
196 }
197
198 //---------------------------------------------------------------------
199
200 // public
calculate(int isX,int dir)201 bool kpTransformAutoCropBorder::calculate (int isX, int dir)
202 {
203 #if DEBUG_KP_TOOL_AUTO_CROP && 1
204 qCDebug(kpLogImagelib) << "kpTransformAutoCropBorder::calculate() CALLED!";
205 #endif
206 int maxX = m_imagePtr->width () - 1;
207 int maxY = m_imagePtr->height () - 1;
208
209 QImage qimage = *m_imagePtr;
210 Q_ASSERT (!qimage.isNull ());
211
212 // (sync both branches)
213 if (isX)
214 {
215 int numCols = 0;
216 int startX = (dir > 0) ? 0 : maxX;
217
218 kpColor col = kpPixmapFX::getColorAtPixel (qimage, startX, 0);
219 for (int x = startX;
220 x >= 0 && x <= maxX;
221 x += dir)
222 {
223 int y;
224 for (y = 0; y <= maxY; y++)
225 {
226 if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity))
227 break;
228 }
229
230 if (y <= maxY)
231 break;
232 else
233 numCols++;
234 }
235
236 if (numCols)
237 {
238 m_rect =
239 kpPainter::normalizedRect(QPoint(startX, 0),
240 QPoint(startX + (numCols - 1) * dir, maxY));
241 m_referenceColor = col;
242 }
243 }
244 else
245 {
246 int numRows = 0;
247 int startY = (dir > 0) ? 0 : maxY;
248
249 kpColor col = kpPixmapFX::getColorAtPixel (qimage, 0, startY);
250 for (int y = startY;
251 y >= 0 && y <= maxY;
252 y += dir)
253 {
254 int x;
255 for (x = 0; x <= maxX; x++)
256 {
257 if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity))
258 break;
259 }
260
261 if (x <= maxX)
262 break;
263 else
264 numRows++;
265 }
266
267 if (numRows)
268 {
269 m_rect = kpPainter::normalizedRect(QPoint(0, startY),
270 QPoint(maxX, startY + (numRows - 1) * dir));
271 m_referenceColor = col;
272 }
273 }
274
275
276 if (m_rect.isValid ())
277 {
278 m_isSingleColor = true;
279
280 if (m_processedColorSimilarity != 0)
281 {
282 for (int y = m_rect.top (); y <= m_rect.bottom (); y++)
283 {
284 for (int x = m_rect.left (); x <= m_rect.right (); x++)
285 {
286 kpColor colAtPixel = kpPixmapFX::getColorAtPixel (qimage, x, y);
287
288 if (m_isSingleColor && colAtPixel != m_referenceColor)
289 m_isSingleColor = false;
290
291 m_redSum += colAtPixel.red ();
292 m_greenSum += colAtPixel.green ();
293 m_blueSum += colAtPixel.blue ();
294 }
295 }
296 }
297 }
298
299
300 return true;
301 }
302
303 // public
fillsEntireImage() const304 bool kpTransformAutoCropBorder::fillsEntireImage () const
305 {
306 return (m_rect == m_imagePtr->rect ());
307 }
308
309 // public
exists() const310 bool kpTransformAutoCropBorder::exists () const
311 {
312 // (will use in an addition so make sure returns 1 or 0)
313 return (m_rect.isValid () ? 1 : 0);
314 }
315
316 // public
invalidate()317 void kpTransformAutoCropBorder::invalidate ()
318 {
319 m_rect = QRect ();
320 m_referenceColor = kpColor::Invalid;
321 m_redSum = m_greenSum = m_blueSum = 0;
322 m_isSingleColor = false;
323 }
324
325
326 struct kpTransformAutoCropCommandPrivate
327 {
328 bool actOnSelection{};
329 kpTransformAutoCropBorder leftBorder, rightBorder, topBorder, botBorder;
330 kpImage *leftImage{}, *rightImage{}, *topImage{}, *botImage{};
331
332 QRect contentsRect;
333 int oldWidth{}, oldHeight{};
334 kpAbstractImageSelection *oldSelectionPtr{};
335 };
336
337 // REFACTOR: Move to /commands/
kpTransformAutoCropCommand(bool actOnSelection,const kpTransformAutoCropBorder & leftBorder,const kpTransformAutoCropBorder & rightBorder,const kpTransformAutoCropBorder & topBorder,const kpTransformAutoCropBorder & botBorder,kpCommandEnvironment * environ)338 kpTransformAutoCropCommand::kpTransformAutoCropCommand (bool actOnSelection,
339 const kpTransformAutoCropBorder &leftBorder,
340 const kpTransformAutoCropBorder &rightBorder,
341 const kpTransformAutoCropBorder &topBorder,
342 const kpTransformAutoCropBorder &botBorder,
343 kpCommandEnvironment *environ)
344 : kpNamedCommand(text(actOnSelection, DontShowAccel), environ),
345 d (new kpTransformAutoCropCommandPrivate ())
346 {
347 d->actOnSelection = actOnSelection;
348 d->leftBorder = leftBorder;
349 d->rightBorder = rightBorder;
350 d->topBorder = topBorder;
351 d->botBorder = botBorder;
352 d->leftImage = nullptr;
353 d->rightImage = nullptr;
354 d->topImage = nullptr;
355 d->botImage = nullptr;
356
357 kpDocument *doc = document ();
358 Q_ASSERT (doc);
359
360 d->oldWidth = doc->width (d->actOnSelection);
361 d->oldHeight = doc->height (d->actOnSelection);
362
363 d->oldSelectionPtr = nullptr;
364 }
365
366 //---------------------------------------------------------------------
367
~kpTransformAutoCropCommand()368 kpTransformAutoCropCommand::~kpTransformAutoCropCommand ()
369 {
370 deleteUndoImages ();
371
372 delete d->oldSelectionPtr;
373 delete d;
374 }
375
376 //---------------------------------------------------------------------
377 // public static
378
text(bool actOnSelection,int options)379 QString kpTransformAutoCropCommand::text(bool actOnSelection, int options)
380 {
381 if (actOnSelection)
382 {
383 if (options & kpTransformAutoCropCommand::ShowAccel) {
384 return i18n ("Remove Internal B&order");
385 }
386
387 return i18n ("Remove Internal Border");
388 }
389
390 if (options & kpTransformAutoCropCommand::ShowAccel)
391 return i18n ("Autocr&op");
392
393 return i18n ("Autocrop");
394 }
395
396 //---------------------------------------------------------------------
397 // public virtual [base kpCommand]
398
size() const399 kpCommandSize::SizeType kpTransformAutoCropCommand::size () const
400 {
401 return d->leftBorder.size () +
402 d->rightBorder.size () +
403 d->topBorder.size () +
404 d->botBorder.size () +
405 ImageSize (d->leftImage) +
406 ImageSize (d->rightImage) +
407 ImageSize (d->topImage) +
408 ImageSize (d->botImage) +
409 SelectionSize (d->oldSelectionPtr);
410 }
411
412 //---------------------------------------------------------------------
413 // private
414
getUndoImage(const kpTransformAutoCropBorder & border,kpImage ** image)415 void kpTransformAutoCropCommand::getUndoImage (const kpTransformAutoCropBorder &border, kpImage **image)
416 {
417 kpDocument *doc = document ();
418 Q_ASSERT (doc);
419
420 #if DEBUG_KP_TOOL_AUTO_CROP && 1
421 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::getUndoImage()";
422 qCDebug(kpLogImagelib) << "\timage=" << image
423 << " border: rect=" << border.rect ()
424 << " isSingleColor=" << border.isSingleColor ();
425 #endif
426
427 if (image && border.exists () && !border.isSingleColor ())
428 {
429 if (*image)
430 {
431 #if DEBUG_KP_TOOL_AUTO_CROP && 1
432 qCDebug(kpLogImagelib) << "\talready have *image - delete it";
433 #endif
434 delete *image;
435 }
436
437 *image = new kpImage (
438 kpPixmapFX::getPixmapAt (doc->image (d->actOnSelection),
439 border.rect ()));
440 }
441 }
442
443
444 // private
getUndoImages()445 void kpTransformAutoCropCommand::getUndoImages ()
446 {
447 getUndoImage (d->leftBorder, &d->leftImage);
448 getUndoImage (d->rightBorder, &d->rightImage);
449 getUndoImage (d->topBorder, &d->topImage);
450 getUndoImage (d->botBorder, &d->botImage);
451 }
452
453 // private
deleteUndoImages()454 void kpTransformAutoCropCommand::deleteUndoImages ()
455 {
456 #if DEBUG_KP_TOOL_AUTO_CROP && 1
457 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::deleteUndoImages()";
458 #endif
459
460 delete d->leftImage; d->leftImage = nullptr;
461 delete d->rightImage; d->rightImage = nullptr;
462 delete d->topImage; d->topImage = nullptr;
463 delete d->botImage; d->botImage = nullptr;
464 }
465
466
467 // public virtual [base kpCommand]
execute()468 void kpTransformAutoCropCommand::execute ()
469 {
470 if (!d->contentsRect.isValid ()) {
471 d->contentsRect = contentsRect ();
472 }
473
474
475 getUndoImages ();
476
477
478 kpDocument *doc = document ();
479 Q_ASSERT (doc);
480
481
482 kpImage imageWithoutBorder =
483 kpTool::neededPixmap (doc->image (d->actOnSelection),
484 d->contentsRect);
485
486
487 if (!d->actOnSelection) {
488 doc->setImage (imageWithoutBorder);
489 }
490 else {
491 d->oldSelectionPtr = doc->imageSelection ()->clone ();
492 d->oldSelectionPtr->setBaseImage (kpImage ());
493
494 // d->contentsRect is relative to the top of the sel
495 // while sel is relative to the top of the doc
496 QRect rect = d->contentsRect;
497 rect.translate (d->oldSelectionPtr->x (), d->oldSelectionPtr->y ());
498
499 kpRectangularImageSelection sel (
500 rect,
501 imageWithoutBorder,
502 d->oldSelectionPtr->transparency ());
503
504 doc->setSelection (sel);
505
506 environ ()->somethingBelowTheCursorChanged ();
507 }
508 }
509
510 // public virtual [base kpCommand]
unexecute()511 void kpTransformAutoCropCommand::unexecute ()
512 {
513 #if DEBUG_KP_TOOL_AUTO_CROP && 1
514 qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::unexecute()";
515 #endif
516
517 kpDocument *doc = document ();
518 Q_ASSERT (doc);
519
520 kpImage image (d->oldWidth, d->oldHeight, QImage::Format_ARGB32_Premultiplied);
521
522 // restore the position of the center image
523 kpPixmapFX::setPixmapAt (&image, d->contentsRect,
524 doc->image (d->actOnSelection));
525
526 // draw the borders
527
528 const kpTransformAutoCropBorder *borders [] =
529 {
530 &d->leftBorder, &d->rightBorder,
531 &d->topBorder, &d->botBorder,
532 nullptr
533 };
534
535 const kpImage *images [] =
536 {
537 d->leftImage, d->rightImage,
538 d->topImage, d->botImage,
539 nullptr
540 };
541
542 const kpImage **p = images;
543 for (const kpTransformAutoCropBorder **b = borders; *b; b++, p++)
544 {
545 if (!(*b)->exists ()) {
546 continue;
547 }
548
549 if ((*b)->isSingleColor ())
550 {
551 kpColor col = (*b)->referenceColor ();
552 #if DEBUG_KP_TOOL_AUTO_CROP && 1
553 qCDebug(kpLogImagelib) << "\tdrawing border " << (*b)->rect ()
554 << " rgb=" << (int *) col.toQRgb () /* %X hack */;
555 #endif
556
557 const QRect r = (*b)->rect ();
558 kpPainter::fillRect (&image,
559 r.x (), r.y (), r.width (), r.height (),
560 col);
561 }
562 else
563 {
564 #if DEBUG_KP_TOOL_AUTO_CROP && 1
565 qCDebug(kpLogImagelib) << "\trestoring border image " << (*b)->rect ();
566 #endif
567 if (*p)
568 {
569 // REFACTOR: Add equivalent method to kpPainter and use.
570 kpPixmapFX::setPixmapAt (&image, (*b)->rect (), **p);
571 }
572 }
573 }
574
575
576 if (!d->actOnSelection) {
577 doc->setImage (image);
578 }
579 else
580 {
581 d->oldSelectionPtr->setBaseImage (image);
582
583 doc->setSelection (*d->oldSelectionPtr);
584 delete d->oldSelectionPtr; d->oldSelectionPtr = nullptr;
585
586 environ ()->somethingBelowTheCursorChanged ();
587 }
588
589
590 deleteUndoImages ();
591 }
592
593
594 // private
contentsRect() const595 QRect kpTransformAutoCropCommand::contentsRect () const
596 {
597 const kpImage image = document ()->image (d->actOnSelection);
598
599 QPoint topLeft (d->leftBorder.exists () ?
600 d->leftBorder.rect ().right () + 1 :
601 0,
602 d->topBorder.exists () ?
603 d->topBorder.rect ().bottom () + 1 :
604 0);
605 QPoint botRight (d->rightBorder.exists () ?
606 d->rightBorder.rect ().left () - 1 :
607 image.width () - 1,
608 d->botBorder.exists () ?
609 d->botBorder.rect ().top () - 1 :
610 image.height () - 1);
611
612 return {topLeft, botRight};
613 }
614
615
ShowNothingToAutocropMessage(kpMainWindow * mainWindow,bool actOnSelection)616 static void ShowNothingToAutocropMessage (kpMainWindow *mainWindow, bool actOnSelection)
617 {
618 kpSetOverrideCursorSaver cursorSaver (Qt::ArrowCursor);
619
620 if (actOnSelection)
621 {
622 KMessageBox::information (mainWindow,
623 i18n ("KolourPaint cannot remove the selection's internal border as it"
624 " could not be located."),
625 i18nc ("@title:window", "Cannot Remove Internal Border"),
626 QStringLiteral("NothingToAutoCrop"));
627 }
628 else
629 {
630 KMessageBox::information (mainWindow,
631 i18n ("KolourPaint cannot automatically crop the image as its"
632 " border could not be located."),
633 i18nc ("@title:window", "Cannot Autocrop"),
634 QStringLiteral("NothingToAutoCrop"));
635 }
636 }
637
kpTransformAutoCrop(kpMainWindow * mainWindow)638 bool kpTransformAutoCrop (kpMainWindow *mainWindow)
639 {
640 #if DEBUG_KP_TOOL_AUTO_CROP
641 qCDebug(kpLogImagelib) << "kpTransformAutoCrop() CALLED!";
642 #endif
643
644 Q_ASSERT (mainWindow);
645 kpDocument *doc = mainWindow->document ();
646 Q_ASSERT (doc);
647
648 // OPT: if already pulled selection image, no need to do it again here
649 kpImage image = doc->selection () ? doc->getSelectedBaseImage () : doc->image ();
650 Q_ASSERT (!image.isNull ());
651
652 kpViewManager *vm = mainWindow->viewManager ();
653 Q_ASSERT (vm);
654
655 int processedColorSimilarity = mainWindow->colorToolBar ()->processedColorSimilarity ();
656 kpTransformAutoCropBorder leftBorder (&image, processedColorSimilarity),
657 rightBorder (&image, processedColorSimilarity),
658 topBorder (&image, processedColorSimilarity),
659 botBorder (&image, processedColorSimilarity);
660
661
662 kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
663
664 mainWindow->colorToolBar ()->flashColorSimilarityToolBarItem ();
665
666 // TODO: With Colour Similarity, a lot of weird (and wonderful) things can
667 // happen resulting in a huge number of code paths. Needs refactoring
668 // and regression testing.
669 //
670 // TODO: e.g. When the top fills entire rect but bot doesn't we could
671 // invalidate top and continue autocrop.
672 int numRegions = 0;
673 if (!leftBorder.calculate (true/*x*/, +1/*going right*/) ||
674 leftBorder.fillsEntireImage () ||
675 !rightBorder.calculate (true/*x*/, -1/*going left*/) ||
676 rightBorder.fillsEntireImage () ||
677 !topBorder.calculate (false/*y*/, +1/*going down*/) ||
678 topBorder.fillsEntireImage () ||
679 !botBorder.calculate (false/*y*/, -1/*going up*/) ||
680 botBorder.fillsEntireImage () ||
681 ((numRegions = leftBorder.exists () +
682 rightBorder.exists () +
683 topBorder.exists () +
684 botBorder.exists ()) == 0))
685 {
686 #if DEBUG_KP_TOOL_AUTO_CROP
687 qCDebug(kpLogImagelib) << "\tcan't find border; leftBorder.rect=" << leftBorder.rect ()
688 << " rightBorder.rect=" << rightBorder.rect ()
689 << " topBorder.rect=" << topBorder.rect ()
690 << " botBorder.rect=" << botBorder.rect ();
691 #endif
692 ::ShowNothingToAutocropMessage (mainWindow, static_cast<bool> (doc->selection ()));
693 return false;
694 }
695
696 #if DEBUG_KP_TOOL_AUTO_CROP
697 qCDebug(kpLogImagelib) << "\tnumRegions=" << numRegions;
698 qCDebug(kpLogImagelib) << "\t\tleft=" << leftBorder.rect ()
699 << " refCol=" << (leftBorder.exists () ? (int *) leftBorder.referenceColor ().toQRgb () : nullptr)
700 << " avgCol=" << (leftBorder.exists () ? (int *) leftBorder.averageColor ().toQRgb () : nullptr);
701 qCDebug(kpLogImagelib) << "\t\tright=" << rightBorder.rect ()
702 << " refCol=" << (rightBorder.exists () ? (int *) rightBorder.referenceColor ().toQRgb () : nullptr)
703 << " avgCol=" << (rightBorder.exists () ? (int *) rightBorder.averageColor ().toQRgb () : nullptr);
704 qCDebug(kpLogImagelib) << "\t\ttop=" << topBorder.rect ()
705 << " refCol=" << (topBorder.exists () ? (int *) topBorder.referenceColor ().toQRgb () : nullptr)
706 << " avgCol=" << (topBorder.exists () ? (int *) topBorder.averageColor ().toQRgb () : nullptr);
707 qCDebug(kpLogImagelib) << "\t\tbot=" << botBorder.rect ()
708 << " refCol=" << (botBorder.exists () ? (int *) botBorder.referenceColor ().toQRgb () : nullptr)
709 << " avgCol=" << (botBorder.exists () ? (int *) botBorder.averageColor ().toQRgb () : nullptr);
710 #endif
711
712 // In case e.g. the user pastes a solid, coloured-in rectangle,
713 // we favor killing the bottom and right regions
714 // (these regions probably contain the unwanted whitespace due
715 // to the doc being bigger than the pasted selection to start with).
716 //
717 // We also kill if they kiss or even overlap.
718
719 if (leftBorder.exists () && rightBorder.exists ())
720 {
721 const kpColor leftCol = leftBorder.averageColor ();
722 const kpColor rightCol = rightBorder.averageColor ();
723
724 if ((numRegions == 2 && !leftCol.isSimilarTo (rightCol, processedColorSimilarity)) ||
725 leftBorder.right () >= rightBorder.left () - 1) // kissing or overlapping
726 {
727 #if DEBUG_KP_TOOL_AUTO_CROP
728 qCDebug(kpLogImagelib) << "\tignoring left border";
729 #endif
730 leftBorder.invalidate ();
731 }
732 }
733
734 if (topBorder.exists () && botBorder.exists ())
735 {
736 const kpColor topCol = topBorder.averageColor ();
737 const kpColor botCol = botBorder.averageColor ();
738
739 if ((numRegions == 2 && !topCol.isSimilarTo (botCol, processedColorSimilarity)) ||
740 topBorder.bottom () >= botBorder.top () - 1) // kissing or overlapping
741 {
742 #if DEBUG_KP_TOOL_AUTO_CROP
743 qCDebug(kpLogImagelib) << "\tignoring top border";
744 #endif
745 topBorder.invalidate ();
746 }
747 }
748
749
750 mainWindow->addImageOrSelectionCommand (
751 new kpTransformAutoCropCommand (static_cast<bool> (doc->selection ()),
752 leftBorder, rightBorder, topBorder, botBorder, mainWindow->commandEnvironment ()));
753
754
755 return true;
756 }
757