File indexing completed on 2024-06-02 04:23:53

0001 
0002 /*
0003    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 // TODO: Color Similarity is obviously useful in Autocrop but it isn't
0029 //       obvious as to how to implement it.  The current heuristic,
0030 //       for each side, chooses an arbitrary reference color for which
0031 //       all other candidate pixels in that side are tested against
0032 //       for similarity.  But if the reference color happens to be at
0033 //       one extreme of the range of colors in that side, then pixels
0034 //       at the other extreme would not be deemed similar enough.  The
0035 //       key is to find the median color as the reference but how do
0036 //       you do this if you don't know which pixels to sample in the first
0037 //       place (that's what you're trying to find)?  Chicken and egg situation.
0038 //
0039 //       The other heuristic that is in doubt is the use of the average
0040 //       color in determining the similarity of sides (it is possible
0041 //       to get vastly differently colors in both sides yet they will be
0042 //       considered similar).
0043 
0044 #define DEBUG_KP_TOOL_AUTO_CROP 0
0045 
0046 
0047 #include "kpTransformAutoCrop.h"
0048 
0049 #include "layers/selections/image/kpAbstractImageSelection.h"
0050 #include "widgets/toolbars/kpColorToolBar.h"
0051 #include "environments/commands/kpCommandEnvironment.h"
0052 #include "commands/kpCommandHistory.h"
0053 #include "document/kpDocument.h"
0054 #include "mainWindow/kpMainWindow.h"
0055 #include "imagelib/kpPainter.h"
0056 #include "pixmapfx/kpPixmapFX.h"
0057 #include "layers/selections/image/kpRectangularImageSelection.h"
0058 #include "generic/kpSetOverrideCursorSaver.h"
0059 #include "tools/kpTool.h"
0060 #include "views/manager/kpViewManager.h"
0061 
0062 #include "kpLogCategories.h"
0063 #include <KMessageBox>
0064 #include <KLocalizedString>
0065 
0066 #include <QImage>
0067 
0068 //---------------------------------------------------------------------
0069 
0070 class kpTransformAutoCropBorder
0071 {
0072 public:
0073     // WARNING: Only call the <ctor> with imagePtr = 0 if you are going to use
0074     //          operator= to fill it in with a valid imagePtr immediately
0075     //          afterwards.
0076     explicit kpTransformAutoCropBorder (const kpImage *imagePtr = nullptr, int processedColorSimilarity = 0);
0077 
0078     kpCommandSize::SizeType size () const;
0079 
0080     const kpImage *image () const;
0081     int processedColorSimilarity () const;
0082     QRect rect () const;
0083     int left () const;
0084     int right () const;
0085     int top () const;
0086     int bottom () const;
0087     kpColor referenceColor () const;
0088     kpColor averageColor () const;
0089     bool isSingleColor () const;
0090 
0091     // (returns true on success (even if no rect) or false on error)
0092     bool calculate (int isX, int dir);
0093 
0094     bool fillsEntireImage () const;
0095     bool exists () const;
0096     void invalidate ();
0097 
0098 private:
0099     const kpImage *m_imagePtr;
0100     int m_processedColorSimilarity;
0101 
0102     QRect m_rect;
0103     kpColor m_referenceColor;
0104     int m_redSum, m_greenSum, m_blueSum;
0105     bool m_isSingleColor;
0106 };
0107 
0108 kpTransformAutoCropBorder::kpTransformAutoCropBorder (const kpImage *imagePtr,
0109                                             int processedColorSimilarity)
0110     : m_imagePtr (imagePtr),
0111       m_processedColorSimilarity (processedColorSimilarity)
0112 {
0113     invalidate ();
0114 }
0115 
0116 
0117 // public
0118 kpCommandSize::SizeType kpTransformAutoCropBorder::size () const
0119 {
0120     return sizeof (kpTransformAutoCropBorder);
0121 }
0122 
0123 
0124 // public
0125 const kpImage *kpTransformAutoCropBorder::image () const
0126 {
0127     return m_imagePtr;
0128 }
0129 
0130 // public
0131 int kpTransformAutoCropBorder::processedColorSimilarity () const
0132 {
0133     return m_processedColorSimilarity;
0134 }
0135 
0136 // public
0137 QRect kpTransformAutoCropBorder::rect () const
0138 {
0139     return m_rect;
0140 }
0141 
0142 // public
0143 int kpTransformAutoCropBorder::left () const
0144 {
0145     return m_rect.left ();
0146 }
0147 
0148 // public
0149 int kpTransformAutoCropBorder::right () const
0150 {
0151     return m_rect.right ();
0152 }
0153 
0154 // public
0155 int kpTransformAutoCropBorder::top () const
0156 {
0157     return m_rect.top ();
0158 }
0159 
0160 // public
0161 int kpTransformAutoCropBorder::bottom () const
0162 {
0163     return m_rect.bottom ();
0164 }
0165 
0166 // public
0167 kpColor kpTransformAutoCropBorder::referenceColor () const
0168 {
0169     return m_referenceColor;
0170 }
0171 
0172 // public
0173 kpColor kpTransformAutoCropBorder::averageColor () const
0174 {
0175     if (!m_rect.isValid ())
0176         return kpColor::Invalid;
0177 
0178     if (m_referenceColor.isTransparent ())
0179         return kpColor::Transparent;
0180 
0181     if (m_processedColorSimilarity == 0)
0182         return m_referenceColor;
0183 
0184     int numPixels = (m_rect.width () * m_rect.height ());
0185     Q_ASSERT (numPixels > 0);
0186 
0187     return kpColor (m_redSum / numPixels, m_greenSum / numPixels, m_blueSum / numPixels);
0188 
0189 }
0190 
0191 //---------------------------------------------------------------------
0192 
0193 bool kpTransformAutoCropBorder::isSingleColor () const
0194 {
0195     return m_isSingleColor;
0196 }
0197 
0198 //---------------------------------------------------------------------
0199 
0200 // public
0201 bool kpTransformAutoCropBorder::calculate (int isX, int dir)
0202 {
0203 #if DEBUG_KP_TOOL_AUTO_CROP && 1
0204     qCDebug(kpLogImagelib) << "kpTransformAutoCropBorder::calculate() CALLED!";
0205 #endif
0206     int maxX = m_imagePtr->width () - 1;
0207     int maxY = m_imagePtr->height () - 1;
0208 
0209     QImage qimage = *m_imagePtr;
0210     Q_ASSERT (!qimage.isNull ());
0211 
0212     // (sync both branches)
0213     if (isX)
0214     {
0215         int numCols = 0;
0216         int startX = (dir > 0) ? 0 : maxX;
0217 
0218         kpColor col = kpPixmapFX::getColorAtPixel (qimage, startX, 0);
0219         for (int x = startX;
0220              x >= 0 && x <= maxX;
0221              x += dir)
0222         {
0223             int y;
0224             for (y = 0; y <= maxY; y++)
0225             {
0226                 if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity))
0227                     break;
0228             }
0229 
0230             if (y <= maxY)
0231                 break;
0232             else
0233                 numCols++;
0234         }
0235 
0236         if (numCols)
0237         {
0238             m_rect =
0239                 kpPainter::normalizedRect(QPoint(startX, 0),
0240                        QPoint(startX + (numCols - 1) * dir, maxY));
0241             m_referenceColor = col;
0242         }
0243     }
0244     else
0245     {
0246         int numRows = 0;
0247         int startY = (dir > 0) ? 0 : maxY;
0248 
0249         kpColor col = kpPixmapFX::getColorAtPixel (qimage, 0, startY);
0250         for (int y = startY;
0251              y >= 0 && y <= maxY;
0252              y += dir)
0253         {
0254             int x;
0255             for (x = 0; x <= maxX; x++)
0256             {
0257                 if (!kpPixmapFX::getColorAtPixel (qimage, x, y).isSimilarTo (col, m_processedColorSimilarity))
0258                     break;
0259             }
0260 
0261             if (x <= maxX)
0262                 break;
0263             else
0264                 numRows++;
0265         }
0266 
0267         if (numRows)
0268         {
0269             m_rect = kpPainter::normalizedRect(QPoint(0, startY),
0270                          QPoint(maxX, startY + (numRows - 1) * dir));
0271             m_referenceColor = col;
0272         }
0273     }
0274 
0275 
0276     if (m_rect.isValid ())
0277     {
0278         m_isSingleColor = true;
0279 
0280         if (m_processedColorSimilarity != 0)
0281         {
0282             for (int y = m_rect.top (); y <= m_rect.bottom (); y++)
0283             {
0284                 for (int x = m_rect.left (); x <= m_rect.right (); x++)
0285                 {
0286                     kpColor colAtPixel = kpPixmapFX::getColorAtPixel (qimage, x, y);
0287 
0288                     if (m_isSingleColor && colAtPixel != m_referenceColor)
0289                         m_isSingleColor = false;
0290 
0291                     m_redSum += colAtPixel.red ();
0292                     m_greenSum += colAtPixel.green ();
0293                     m_blueSum += colAtPixel.blue ();
0294                 }
0295             }
0296         }
0297     }
0298 
0299 
0300     return true;
0301 }
0302 
0303 // public
0304 bool kpTransformAutoCropBorder::fillsEntireImage () const
0305 {
0306     return (m_rect == m_imagePtr->rect ());
0307 }
0308 
0309 // public
0310 bool kpTransformAutoCropBorder::exists () const
0311 {
0312     // (will use in an addition so make sure returns 1 or 0)
0313     return (m_rect.isValid () ? 1 : 0);
0314 }
0315 
0316 // public
0317 void kpTransformAutoCropBorder::invalidate ()
0318 {
0319     m_rect = QRect ();
0320     m_referenceColor = kpColor::Invalid;
0321     m_redSum = m_greenSum = m_blueSum = 0;
0322     m_isSingleColor = false;
0323 }
0324 
0325 
0326 struct kpTransformAutoCropCommandPrivate
0327 {
0328     bool actOnSelection{};
0329     kpTransformAutoCropBorder leftBorder, rightBorder, topBorder, botBorder;
0330     kpImage *leftImage{}, *rightImage{}, *topImage{}, *botImage{};
0331 
0332     QRect contentsRect;
0333     int oldWidth{}, oldHeight{};
0334     kpAbstractImageSelection *oldSelectionPtr{};
0335 };
0336 
0337 // REFACTOR: Move to /commands/
0338 kpTransformAutoCropCommand::kpTransformAutoCropCommand (bool actOnSelection,
0339         const kpTransformAutoCropBorder &leftBorder,
0340         const kpTransformAutoCropBorder &rightBorder,
0341         const kpTransformAutoCropBorder &topBorder,
0342         const kpTransformAutoCropBorder &botBorder,
0343         kpCommandEnvironment *environ)
0344     : kpNamedCommand(text(actOnSelection, DontShowAccel), environ),
0345       d (new kpTransformAutoCropCommandPrivate ())
0346 {
0347     d->actOnSelection = actOnSelection;
0348     d->leftBorder = leftBorder;
0349     d->rightBorder = rightBorder;
0350     d->topBorder = topBorder;
0351     d->botBorder = botBorder;
0352     d->leftImage = nullptr;
0353     d->rightImage = nullptr;
0354     d->topImage = nullptr;
0355     d->botImage = nullptr;
0356 
0357     kpDocument *doc = document ();
0358     Q_ASSERT (doc);
0359 
0360     d->oldWidth = doc->width (d->actOnSelection);
0361     d->oldHeight = doc->height (d->actOnSelection);
0362 
0363     d->oldSelectionPtr = nullptr;
0364 }
0365 
0366 //---------------------------------------------------------------------
0367 
0368 kpTransformAutoCropCommand::~kpTransformAutoCropCommand ()
0369 {
0370     deleteUndoImages ();
0371 
0372     delete d->oldSelectionPtr;
0373     delete d;
0374 }
0375 
0376 //---------------------------------------------------------------------
0377 // public static
0378 
0379 QString kpTransformAutoCropCommand::text(bool actOnSelection, int options)
0380 {
0381     if (actOnSelection)
0382     {
0383         if (options & kpTransformAutoCropCommand::ShowAccel) {
0384             return i18n ("Remove Internal B&order");
0385         }
0386 
0387         return i18n ("Remove Internal Border");
0388     }
0389 
0390     if (options & kpTransformAutoCropCommand::ShowAccel)
0391         return i18n ("Autocr&op");
0392 
0393     return i18n ("Autocrop");
0394 }
0395 
0396 //---------------------------------------------------------------------
0397 // public virtual [base kpCommand]
0398 
0399 kpCommandSize::SizeType kpTransformAutoCropCommand::size () const
0400 {
0401     return d->leftBorder.size () +
0402            d->rightBorder.size () +
0403            d->topBorder.size () +
0404            d->botBorder.size () +
0405            ImageSize (d->leftImage) +
0406            ImageSize (d->rightImage) +
0407            ImageSize (d->topImage) +
0408            ImageSize (d->botImage) +
0409            SelectionSize (d->oldSelectionPtr);
0410 }
0411 
0412 //---------------------------------------------------------------------
0413 // private
0414 
0415 void kpTransformAutoCropCommand::getUndoImage (const kpTransformAutoCropBorder &border, kpImage **image)
0416 {
0417     kpDocument *doc = document ();
0418     Q_ASSERT (doc);
0419 
0420 #if DEBUG_KP_TOOL_AUTO_CROP && 1
0421     qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::getUndoImage()";
0422     qCDebug(kpLogImagelib) << "\timage=" << image
0423                << " border: rect=" << border.rect ()
0424                << " isSingleColor=" << border.isSingleColor ();
0425 #endif
0426 
0427     if (image && border.exists () && !border.isSingleColor ())
0428     {
0429         if (*image)
0430         {
0431         #if DEBUG_KP_TOOL_AUTO_CROP && 1
0432             qCDebug(kpLogImagelib) << "\talready have *image - delete it";
0433         #endif
0434             delete *image;
0435         }
0436 
0437         *image = new kpImage (
0438             kpPixmapFX::getPixmapAt (doc->image (d->actOnSelection),
0439                                      border.rect ()));
0440     }
0441 }
0442 
0443 
0444 // private
0445 void kpTransformAutoCropCommand::getUndoImages ()
0446 {
0447     getUndoImage (d->leftBorder, &d->leftImage);
0448     getUndoImage (d->rightBorder, &d->rightImage);
0449     getUndoImage (d->topBorder, &d->topImage);
0450     getUndoImage (d->botBorder, &d->botImage);
0451 }
0452 
0453 // private
0454 void kpTransformAutoCropCommand::deleteUndoImages ()
0455 {
0456 #if DEBUG_KP_TOOL_AUTO_CROP && 1
0457     qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::deleteUndoImages()";
0458 #endif
0459 
0460     delete d->leftImage; d->leftImage = nullptr;
0461     delete d->rightImage; d->rightImage = nullptr;
0462     delete d->topImage; d->topImage = nullptr;
0463     delete d->botImage; d->botImage = nullptr;
0464 }
0465 
0466 
0467 // public virtual [base kpCommand]
0468 void kpTransformAutoCropCommand::execute ()
0469 {
0470     if (!d->contentsRect.isValid ()) {
0471         d->contentsRect = contentsRect ();
0472     }
0473 
0474 
0475     getUndoImages ();
0476 
0477 
0478     kpDocument *doc = document ();
0479     Q_ASSERT (doc);
0480 
0481 
0482     kpImage imageWithoutBorder =
0483         kpTool::neededPixmap (doc->image (d->actOnSelection),
0484                               d->contentsRect);
0485 
0486 
0487     if (!d->actOnSelection) {
0488         doc->setImage (imageWithoutBorder);
0489     }
0490     else {
0491         d->oldSelectionPtr = doc->imageSelection ()->clone ();
0492         d->oldSelectionPtr->setBaseImage (kpImage ());
0493 
0494         // d->contentsRect is relative to the top of the sel
0495         // while sel is relative to the top of the doc
0496         QRect rect = d->contentsRect;
0497         rect.translate (d->oldSelectionPtr->x (), d->oldSelectionPtr->y ());
0498 
0499         kpRectangularImageSelection sel (
0500             rect,
0501             imageWithoutBorder,
0502             d->oldSelectionPtr->transparency ());
0503 
0504         doc->setSelection (sel);
0505 
0506         environ ()->somethingBelowTheCursorChanged ();
0507     }
0508 }
0509 
0510 // public virtual [base kpCommand]
0511 void kpTransformAutoCropCommand::unexecute ()
0512 {
0513 #if DEBUG_KP_TOOL_AUTO_CROP && 1
0514     qCDebug(kpLogImagelib) << "kpTransformAutoCropCommand::unexecute()";
0515 #endif
0516 
0517     kpDocument *doc = document ();
0518     Q_ASSERT (doc);
0519 
0520     kpImage image (d->oldWidth, d->oldHeight, QImage::Format_ARGB32_Premultiplied);
0521 
0522     // restore the position of the center image
0523     kpPixmapFX::setPixmapAt (&image, d->contentsRect,
0524         doc->image (d->actOnSelection));
0525 
0526     // draw the borders
0527 
0528     const kpTransformAutoCropBorder *borders [] =
0529     {
0530         &d->leftBorder, &d->rightBorder,
0531         &d->topBorder, &d->botBorder,
0532         nullptr
0533     };
0534 
0535     const kpImage *images [] =
0536     {
0537         d->leftImage, d->rightImage,
0538         d->topImage, d->botImage,
0539         nullptr
0540     };
0541 
0542     const kpImage **p = images;
0543     for (const kpTransformAutoCropBorder **b = borders; *b; b++, p++)
0544     {
0545         if (!(*b)->exists ()) {
0546             continue;
0547         }
0548 
0549         if ((*b)->isSingleColor ())
0550         {
0551             kpColor col = (*b)->referenceColor ();
0552         #if DEBUG_KP_TOOL_AUTO_CROP && 1
0553             qCDebug(kpLogImagelib) << "\tdrawing border " << (*b)->rect ()
0554                        << " rgb=" << (int *) col.toQRgb () /* %X hack */;
0555         #endif
0556 
0557             const QRect r = (*b)->rect ();
0558             kpPainter::fillRect (&image,
0559                 r.x (), r.y (), r.width (), r.height (),
0560                 col);
0561         }
0562         else
0563         {
0564         #if DEBUG_KP_TOOL_AUTO_CROP && 1
0565             qCDebug(kpLogImagelib) << "\trestoring border image " << (*b)->rect ();
0566         #endif
0567             if (*p)
0568             {
0569                 // REFACTOR: Add equivalent method to kpPainter and use.
0570                 kpPixmapFX::setPixmapAt (&image, (*b)->rect (), **p);
0571             }
0572         }
0573     }
0574 
0575 
0576     if (!d->actOnSelection) {
0577         doc->setImage (image);
0578     }
0579     else
0580     {
0581         d->oldSelectionPtr->setBaseImage (image);
0582 
0583         doc->setSelection (*d->oldSelectionPtr);
0584         delete d->oldSelectionPtr; d->oldSelectionPtr = nullptr;
0585 
0586         environ ()->somethingBelowTheCursorChanged ();
0587     }
0588 
0589 
0590     deleteUndoImages ();
0591 }
0592 
0593 
0594 // private
0595 QRect kpTransformAutoCropCommand::contentsRect () const
0596 {
0597     const kpImage image = document ()->image (d->actOnSelection);
0598 
0599     QPoint topLeft (d->leftBorder.exists () ?
0600                         d->leftBorder.rect ().right () + 1 :
0601                         0,
0602                     d->topBorder.exists () ?
0603                         d->topBorder.rect ().bottom () + 1 :
0604                         0);
0605     QPoint botRight (d->rightBorder.exists () ?
0606                          d->rightBorder.rect ().left () - 1 :
0607                          image.width () - 1,
0608                      d->botBorder.exists () ?
0609                          d->botBorder.rect ().top () - 1 :
0610                          image.height () - 1);
0611 
0612     return {topLeft, botRight};
0613 }
0614 
0615 
0616 static void ShowNothingToAutocropMessage (kpMainWindow *mainWindow, bool actOnSelection)
0617 {
0618     kpSetOverrideCursorSaver cursorSaver (Qt::ArrowCursor);
0619 
0620     if (actOnSelection)
0621     {
0622         KMessageBox::information (mainWindow,
0623             i18n ("KolourPaint cannot remove the selection's internal border as it"
0624                   " could not be located."),
0625             i18nc ("@title:window", "Cannot Remove Internal Border"),
0626             QStringLiteral("NothingToAutoCrop"));
0627     }
0628     else
0629     {
0630         KMessageBox::information (mainWindow,
0631             i18n ("KolourPaint cannot automatically crop the image as its"
0632                   " border could not be located."),
0633             i18nc ("@title:window", "Cannot Autocrop"),
0634             QStringLiteral("NothingToAutoCrop"));
0635     }
0636 }
0637 
0638 bool kpTransformAutoCrop (kpMainWindow *mainWindow)
0639 {
0640 #if DEBUG_KP_TOOL_AUTO_CROP
0641     qCDebug(kpLogImagelib) << "kpTransformAutoCrop() CALLED!";
0642 #endif
0643 
0644     Q_ASSERT (mainWindow);
0645     kpDocument *doc = mainWindow->document ();
0646     Q_ASSERT (doc);
0647 
0648     // OPT: if already pulled selection image, no need to do it again here
0649     kpImage image = doc->selection () ? doc->getSelectedBaseImage () : doc->image ();
0650     Q_ASSERT (!image.isNull ());
0651 
0652     kpViewManager *vm = mainWindow->viewManager ();
0653     Q_ASSERT (vm);
0654 
0655     int processedColorSimilarity = mainWindow->colorToolBar ()->processedColorSimilarity ();
0656     kpTransformAutoCropBorder leftBorder (&image, processedColorSimilarity),
0657                          rightBorder (&image, processedColorSimilarity),
0658                          topBorder (&image, processedColorSimilarity),
0659                          botBorder (&image, processedColorSimilarity);
0660 
0661 
0662     kpSetOverrideCursorSaver cursorSaver (Qt::WaitCursor);
0663 
0664     mainWindow->colorToolBar ()->flashColorSimilarityToolBarItem ();
0665 
0666     // TODO: With Colour Similarity, a lot of weird (and wonderful) things can
0667     //       happen resulting in a huge number of code paths.  Needs refactoring
0668     //       and regression testing.
0669     //
0670     // TODO: e.g. When the top fills entire rect but bot doesn't we could
0671     //       invalidate top and continue autocrop.
0672     int numRegions = 0;
0673     if (!leftBorder.calculate (true/*x*/, +1/*going right*/) ||
0674         leftBorder.fillsEntireImage () ||
0675         !rightBorder.calculate (true/*x*/, -1/*going left*/) ||
0676         rightBorder.fillsEntireImage () ||
0677         !topBorder.calculate (false/*y*/, +1/*going down*/) ||
0678         topBorder.fillsEntireImage () ||
0679         !botBorder.calculate (false/*y*/, -1/*going up*/) ||
0680         botBorder.fillsEntireImage () ||
0681         ((numRegions = leftBorder.exists () +
0682                        rightBorder.exists () +
0683                        topBorder.exists () +
0684                        botBorder.exists ()) == 0))
0685     {
0686     #if DEBUG_KP_TOOL_AUTO_CROP
0687         qCDebug(kpLogImagelib) << "\tcan't find border; leftBorder.rect=" << leftBorder.rect ()
0688                    << " rightBorder.rect=" << rightBorder.rect ()
0689                    << " topBorder.rect=" << topBorder.rect ()
0690                    << " botBorder.rect=" << botBorder.rect ();
0691     #endif
0692         ::ShowNothingToAutocropMessage (mainWindow, static_cast<bool> (doc->selection ()));
0693         return false;
0694     }
0695 
0696 #if DEBUG_KP_TOOL_AUTO_CROP
0697     qCDebug(kpLogImagelib) << "\tnumRegions=" << numRegions;
0698     qCDebug(kpLogImagelib) << "\t\tleft=" << leftBorder.rect ()
0699                << " refCol=" << (leftBorder.exists () ? (int *) leftBorder.referenceColor ().toQRgb () : nullptr)
0700                << " avgCol=" << (leftBorder.exists () ? (int *) leftBorder.averageColor ().toQRgb () : nullptr);
0701     qCDebug(kpLogImagelib) << "\t\tright=" << rightBorder.rect ()
0702                << " refCol=" << (rightBorder.exists () ? (int *) rightBorder.referenceColor ().toQRgb () : nullptr)
0703                << " avgCol=" << (rightBorder.exists () ? (int *) rightBorder.averageColor ().toQRgb () : nullptr);
0704     qCDebug(kpLogImagelib) << "\t\ttop=" << topBorder.rect ()
0705                << " refCol=" << (topBorder.exists () ? (int *) topBorder.referenceColor ().toQRgb () : nullptr)
0706                << " avgCol=" << (topBorder.exists () ? (int *) topBorder.averageColor ().toQRgb () : nullptr);
0707     qCDebug(kpLogImagelib) << "\t\tbot=" << botBorder.rect ()
0708                << " refCol=" << (botBorder.exists () ? (int *) botBorder.referenceColor ().toQRgb () : nullptr)
0709                << " avgCol=" << (botBorder.exists () ? (int *) botBorder.averageColor ().toQRgb () : nullptr);
0710 #endif
0711 
0712     // In case e.g. the user pastes a solid, coloured-in rectangle,
0713     // we favor killing the bottom and right regions
0714     // (these regions probably contain the unwanted whitespace due
0715     //  to the doc being bigger than the pasted selection to start with).
0716     //
0717     // We also kill if they kiss or even overlap.
0718 
0719     if (leftBorder.exists () && rightBorder.exists ())
0720     {
0721         const kpColor leftCol = leftBorder.averageColor ();
0722         const kpColor rightCol = rightBorder.averageColor ();
0723 
0724         if ((numRegions == 2 && !leftCol.isSimilarTo (rightCol, processedColorSimilarity)) ||
0725             leftBorder.right () >= rightBorder.left () - 1)  // kissing or overlapping
0726         {
0727         #if DEBUG_KP_TOOL_AUTO_CROP
0728             qCDebug(kpLogImagelib) << "\tignoring left border";
0729         #endif
0730             leftBorder.invalidate ();
0731         }
0732     }
0733 
0734     if (topBorder.exists () && botBorder.exists ())
0735     {
0736         const kpColor topCol = topBorder.averageColor ();
0737         const kpColor botCol = botBorder.averageColor ();
0738 
0739         if ((numRegions == 2 && !topCol.isSimilarTo (botCol, processedColorSimilarity)) ||
0740             topBorder.bottom () >= botBorder.top () - 1)  // kissing or overlapping
0741         {
0742         #if DEBUG_KP_TOOL_AUTO_CROP
0743             qCDebug(kpLogImagelib) << "\tignoring top border";
0744         #endif
0745             topBorder.invalidate ();
0746         }
0747     }
0748 
0749 
0750     mainWindow->addImageOrSelectionCommand (
0751         new kpTransformAutoCropCommand (static_cast<bool> (doc->selection ()),
0752             leftBorder, rightBorder, topBorder, botBorder,  mainWindow->commandEnvironment ()));
0753 
0754 
0755     return true;
0756 }