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