krita

kis_selection_manager.cc

00001 /*
00002  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include <qobject.h>
00020 #include <qapplication.h>
00021 #include <qclipboard.h>
00022 #include <qcolor.h>
00023 
00024 #include <kdebug.h>
00025 #include <kaction.h>
00026 #include <klocale.h>
00027 #include <kstdaction.h>
00028 
00029 #include <KoDocument.h>
00030 #include <KoMainWindow.h>
00031 #include <KoQueryTrader.h>
00032 
00033 #include "kis_part_layer.h"
00034 #include "kis_clipboard.h"
00035 #include "kis_types.h"
00036 #include "kis_view.h"
00037 #include "kis_doc.h"
00038 #include "kis_image.h"
00039 #include "kis_selection.h"
00040 #include "kis_selection_manager.h"
00041 #include "kis_painter.h"
00042 #include "kis_iterators_pixel.h"
00043 #include "kis_iteratorpixeltrait.h"
00044 #include "kis_layer.h"
00045 #include "kis_group_layer.h"
00046 #include "kis_paint_layer.h"
00047 #include "kis_paint_device.h"
00048 #include "kis_channelinfo.h"
00049 #include "kis_dlg_apply_profile.h"
00050 #include "kis_config.h"
00051 #include "kis_debug_areas.h"
00052 #include "kis_transaction.h"
00053 #include "kis_undo_adapter.h"
00054 #include "kis_selected_transaction.h"
00055 #include "kis_convolution_painter.h"
00056 #include "kis_integer_maths.h"
00057 #include "kis_fill_painter.h"
00058 #include "kis_canvas.h"
00059 
00060 KisSelectionManager::KisSelectionManager(KisView * parent, KisDoc * doc)
00061     : m_parent(parent),
00062       m_doc(doc),
00063       m_copy(0),
00064       m_cut(0),
00065       m_paste(0),
00066       m_pasteNew(0),
00067       m_cutToNewLayer(0),
00068       m_selectAll(0),
00069       m_deselect(0),
00070       m_clear(0),
00071       m_reselect(0),
00072       m_invert(0),
00073       m_toNewLayer(0),
00074       m_feather(0),
00075       m_border(0),
00076       m_expand(0),
00077       m_smooth(0),
00078       m_contract(0),
00079       m_grow(0),
00080       m_similar(0),
00081       m_transform(0),
00082       m_load(0),
00083       m_save(0),
00084       m_fillForegroundColor(0),
00085       m_fillBackgroundColor(0),
00086       m_fillPattern(0)
00087 {
00088     m_pluginActions.setAutoDelete(true);
00089     m_clipboard = KisClipboard::instance();
00090 }
00091 
00092 KisSelectionManager::~KisSelectionManager()
00093 {
00094     m_pluginActions.clear();
00095 }
00096 
00097 
00098 void KisSelectionManager::setup(KActionCollection * collection)
00099 {
00100     // XXX: setup shortcuts!
00101 
00102     m_cut = KStdAction::cut(this,
00103             SLOT(cut()),
00104             collection,
00105             "cut");
00106 
00107     m_copy = KStdAction::copy(this,
00108                 SLOT(copy()),
00109                 collection,
00110                 "copy");
00111 
00112     m_paste = KStdAction::paste(this,
00113                 SLOT(paste()),
00114                 collection,
00115                 "paste");
00116 
00117     m_pasteNew = new KAction(i18n("Paste into &New Image"),
00118                 0, 0,
00119                 this, SLOT(pasteNew()),
00120                 collection,
00121                 "paste_new");
00122 
00123 
00124     m_selectAll = KStdAction::selectAll(this,
00125                     SLOT(selectAll()),
00126                     collection,
00127                     "select_all");
00128 
00129     m_deselect = KStdAction::deselect(this,
00130                     SLOT(deselect()),
00131                     collection,
00132                     "deselect");
00133 
00134 
00135     m_clear = KStdAction::clear(this,
00136                 SLOT(clear()),
00137                 collection,
00138                 "clear");
00139 
00140     m_reselect = new KAction(i18n("&Reselect"),
00141                 0, "Ctrl+Shift+D",
00142                 this, SLOT(reselect()),
00143                 collection, "reselect");
00144 
00145     m_invert = new KAction(i18n("&Invert"),
00146                 0, "Ctrl+I",
00147                 this, SLOT(invert()),
00148                 collection, "invert");
00149 
00150 
00151     m_toNewLayer = new KAction(i18n("Copy Selection to New Layer"),
00152                 0, "Ctrl+J",
00153                 this, SLOT(copySelectionToNewLayer()),
00154                 collection, "copy_selection_to_new_layer");
00155 
00156 
00157     m_cutToNewLayer = new KAction(i18n("Cut Selection to New Layer"),
00158             0, "Ctrl+Shift+J",
00159             this, SLOT(cutToNewLayer()),
00160             collection, "cut_selection_to_new_layer");
00161 
00162     m_feather = new KAction(i18n("Feather"),
00163                 0, "Ctrl+Alt+D",
00164                 this, SLOT(feather()),
00165                 collection, "feather");
00166 
00167     m_fillForegroundColor = new KAction(i18n("Fill with Foreground Color"),
00168                                              "Alt+backspace", this,
00169                                              SLOT(fillForegroundColor()),
00170                                              collection,
00171                                              "fill_selection_foreground_color");
00172     m_fillBackgroundColor = new KAction(i18n("Fill with Background Color"),
00173                                              "backspace", this,
00174                                              SLOT(fillBackgroundColor()),
00175                                              collection,
00176                                              "fill_selection_background_color");
00177     m_fillPattern = new KAction(i18n("Fill with Pattern"),
00178                                              0, this,
00179                                              SLOT(fillPattern()),
00180                                              collection,
00181                                              "fill_selection_pattern");
00182 
00183     m_toggleDisplaySelection = new KToggleAction(i18n("Display Selection"), "Ctrl+h", this, SLOT(toggleDisplaySelection()), collection, "toggle_display_selection");
00184     m_toggleDisplaySelection->setCheckedState(KGuiItem(i18n("Hide Selection")));
00185     m_toggleDisplaySelection->setChecked(true);
00186 
00187     m_border =
00188         new KAction(i18n("Border..."),
00189                 0, 0,
00190                 this, SLOT(border()),
00191                 collection, "border");
00192     m_expand =
00193         new KAction(i18n("Expand..."),
00194                 0, 0,
00195                 this, SLOT(expand()),
00196                 collection, "expand");
00197 
00198     m_smooth =
00199         new KAction(i18n("Smooth..."),
00200                 0, 0,
00201                 this, SLOT(smooth()),
00202                 collection, "smooth");
00203 
00204 
00205     m_contract =
00206         new KAction(i18n("Contract..."),
00207                 0, 0,
00208                 this, SLOT(contract()),
00209                 collection, "contract");
00210     m_grow =
00211         new KAction(i18n("Grow"),
00212                 0, 0,
00213                 this, SLOT(grow()),
00214                 collection, "grow");
00215     m_similar =
00216         new KAction(i18n("Similar"),
00217                 0, 0,
00218                 this, SLOT(similar()),
00219                 collection, "similar");
00220 
00221 
00222     m_transform
00223         = new KAction(i18n("Transform..."),
00224                   0, 0,
00225                   this, SLOT(transform()),
00226                   collection, "transform_selection");
00227 
00228 
00229 //     m_load
00230 //         = new KAction(i18n("Load..."),
00231 //                   0, 0,
00232 //                   this, SLOT(load()),
00233 //                   collection, "load_selection");
00234 //
00235 //
00236 //     m_save
00237 //         = new KAction(i18n("Save As..."),
00238 //                   0, 0,
00239 //                   this, SLOT(save()),
00240 //                   collection, "save_selection");
00241 
00242         QClipboard *cb = QApplication::clipboard();
00243         connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged()));
00244 }
00245 
00246 void KisSelectionManager::clipboardDataChanged()
00247 {
00248     updateGUI();
00249 }
00250 
00251 
00252 void KisSelectionManager::addSelectionAction(KAction * action)
00253 {
00254     m_pluginActions.append(action);
00255 }
00256 
00257 
00258 void KisSelectionManager::updateGUI()
00259 {
00260     Q_ASSERT(m_parent);
00261     Q_ASSERT(m_clipboard);
00262 
00263     if (m_parent == 0) {
00264         // "Eek, no parent!
00265         return;
00266     }
00267 
00268     if (m_clipboard == 0) {
00269         // Eek, no clipboard!
00270         return;
00271     }
00272 
00273     KisImageSP img = m_parent->currentImg();
00274     KisLayerSP l = 0;
00275     KisPaintDeviceSP dev = 0;
00276 
00277     bool enable = false;
00278     if (img && img->activeDevice() && img->activeLayer()) {
00279         l = img->activeLayer();
00280         dev = img->activeDevice();
00281 
00282 
00283         KisPartLayer * partLayer = dynamic_cast<KisPartLayer*>(l.data());
00284         
00285         enable = l && dev&& dev->hasSelection() && !l->locked() && l->visible() && (partLayer==0);
00286 
00287         if(dev)
00288             m_reselect->setEnabled( dev->selectionDeselected() );
00289     }
00290 
00291     m_cut->setEnabled(enable);
00292     m_cutToNewLayer->setEnabled(enable);
00293     m_selectAll->setEnabled(img != 0);
00294     m_deselect->setEnabled(enable);
00295     m_clear->setEnabled(enable);
00296     m_fillForegroundColor->setEnabled(enable);
00297     m_fillBackgroundColor->setEnabled(enable);
00298     m_fillPattern->setEnabled(enable);
00299     m_invert->setEnabled(enable);
00300 
00301     m_feather->setEnabled(enable);
00302 
00303     m_border->setEnabled(enable);
00304     m_expand->setEnabled(enable);
00305     m_smooth->setEnabled(enable);
00306     m_contract->setEnabled(enable);
00307     m_grow->setEnabled(enable);
00308     m_similar->setEnabled(enable);
00309     m_transform->setEnabled(enable);
00310 //    m_load->setEnabled(enable);
00311 //    m_save->setEnabled(enable);
00312 
00313 
00314     KAction * a;
00315     for (a = m_pluginActions.first(); a; a = m_pluginActions.next()) {
00316         a->setEnabled(img != 0);
00317     }
00318 
00319     // You can copy from locked layers and paste the clip into a new layer, even when
00320     // the current layer is locked.
00321     enable = false;
00322     if (img && l && dev) {
00323         enable = dev->hasSelection() && l->visible();
00324     }
00325 
00326     m_copy->setEnabled(enable);
00327     m_paste->setEnabled(img != 0 && m_clipboard->hasClip());
00328     m_pasteNew->setEnabled(img != 0 && m_clipboard->hasClip());
00329     m_toNewLayer->setEnabled(enable);
00330 
00331     m_parent->updateStatusBarSelectionLabel();
00332 
00333 }
00334 
00335 void KisSelectionManager::imgSelectionChanged(KisImageSP img)
00336 {
00337     if (img == m_parent->currentImg()) {
00338         updateGUI();
00339     }
00340 }
00341 
00342 void KisSelectionManager::cut()
00343 {
00344     KisImageSP img = m_parent->currentImg();
00345     if (!img) return;
00346 
00347     KisPaintDeviceSP dev = img->activeDevice();
00348     if (!dev) return;
00349 
00350     if (!dev->hasSelection()) return;
00351 
00352     copy();
00353 
00354     KisSelectedTransaction *t = 0;
00355 
00356     if (img->undo()) {
00357         t = new KisSelectedTransaction(i18n("Cut"), dev);
00358         Q_CHECK_PTR(t);
00359     }
00360 
00361     dev->clearSelection();
00362     dev->deselect();
00363     dev->emitSelectionChanged();
00364 
00365     if (img->undo()) {
00366         img->undoAdapter()->addCommand(t);
00367     }
00368 }
00369 
00370 void KisSelectionManager::copy()
00371 {
00372     KisImageSP img = m_parent->currentImg();
00373     if (!img) return;
00374 
00375     KisPaintDeviceSP dev = img->activeDevice();
00376     if (!dev) return;
00377 
00378     if (!dev->hasSelection()) return;
00379 
00380     KisSelectionSP selection = dev->selection();
00381 
00382     QRect r = selection->selectedExactRect();
00383 
00384     KisPaintDeviceSP clip = new KisPaintDevice(dev->colorSpace(), "clip");
00385     Q_CHECK_PTR(clip);
00386 
00387     KisColorSpace * cs = clip->colorSpace();
00388 
00389     // TODO if the source is linked... copy from all linked layers?!?
00390 
00391     // Copy image data
00392     KisPainter gc;
00393     gc.begin(clip);
00394     gc.bitBlt(0, 0, COMPOSITE_COPY, dev, r.x(), r.y(), r.width(), r.height());
00395     gc.end();
00396 
00397     // Apply selection mask.
00398 
00399     for (Q_INT32 y = 0; y < r.height(); y++) {
00400         KisHLineIterator layerIt = clip->createHLineIterator(0, y, r.width(), true);
00401         KisHLineIterator selectionIt = selection->createHLineIterator(r.x(), r.y() + y, r.width(), false);
00402 
00403         while (!layerIt.isDone()) {
00404 
00405             cs->applyAlphaU8Mask( layerIt.rawData(), selectionIt.rawData(), 1 );
00406 
00407 
00408             ++layerIt;
00409             ++selectionIt;
00410         }
00411     }
00412 
00413     m_clipboard->setClip(clip);
00414     imgSelectionChanged(m_parent->currentImg());
00415 }
00416 
00417 
00418 KisLayerSP KisSelectionManager::paste()
00419 {
00420     KisImageSP img = m_parent->currentImg();
00421     if (!img) return 0;
00422 
00423     KisPaintDeviceSP clip = m_clipboard->clip();
00424 
00425     if (clip) {
00426         KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName() + "(pasted)", OPACITY_OPAQUE);
00427         Q_CHECK_PTR(layer);
00428 
00429         QRect r = clip->exactBounds();
00430         KisPainter gc;
00431         gc.begin(layer->paintDevice());
00432         gc.bitBlt(0, 0, COMPOSITE_COPY, clip, r.x(), r.y(), r.width(), r.height());
00433         gc.end();
00434 
00435        //figure out where to position the clip
00436         KisCanvasController *cc = m_parent->getCanvasController();
00437         QPoint center = cc->viewToWindow(QPoint(cc->kiscanvas()->width()/2, cc->kiscanvas()->height()/2));
00438         QPoint bottomright = cc->viewToWindow(QPoint(cc->kiscanvas()->width(), cc->kiscanvas()->height()));
00439         if(bottomright.x() > img->width())
00440             center.setX(img->width()/2);
00441         if(bottomright.y() > img->height())
00442             center.setY(img->height()/2);
00443         center -= QPoint(r.width()/2, r.height()/2);
00444         layer->setX(center.x());
00445         layer->setY(center.y());
00446 
00447 /*XXX CBR have an idea of asking the user if he is about to paste a clip ion another cs than that of
00448  the image if that is what he want rather than silently converting
00449         if (clip->colorSpace != img ->colorSpace())
00450             if (dlg->exec() == QDialog::Accepted)
00451                 layer->convertTo(img->colorSpace());
00452 */
00453         img->addLayer(layer, img->rootLayer(), img->activeLayer());
00454 
00455         return layer;
00456     }
00457     return 0;
00458 }
00459 
00460 void KisSelectionManager::pasteNew()
00461 {
00462     KisPaintDeviceSP clip = m_clipboard->clip();
00463     if (!clip) return;
00464 
00465     QRect r = clip->exactBounds();
00466     if (r.width() < 1 && r.height() < 1) {
00467         // Don't paste empty clips
00468         return;
00469     }
00470 
00471     const QCString mimetype = KoDocument::readNativeFormatMimeType();
00472     KoDocumentEntry entry = KoDocumentEntry::queryByMimeType( mimetype );
00473     KisDoc * doc = (KisDoc*) entry.createDoc();
00474 
00475     Q_ASSERT(doc->undoAdapter() != 0);
00476     doc->undoAdapter()->setUndo(false);
00477 
00478     KisImageSP img = new KisImage(doc->undoAdapter(), r.width(), r.height(), clip->colorSpace(), "Pasted");
00479     KisPaintLayer *layer = new KisPaintLayer(img, clip->name(), OPACITY_OPAQUE, clip->colorSpace());
00480 
00481     KisPainter p(layer->paintDevice());
00482     p.bitBlt(0, 0, COMPOSITE_COPY, clip, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
00483     p.end();
00484 
00485     img->addLayer(layer, img->rootLayer(), 0);
00486     doc->setCurrentImage(img);
00487 
00488     doc->undoAdapter()->setUndo(true);
00489 
00490     KoMainWindow *win = new KoMainWindow( doc->instance() );
00491     win->show();
00492     win->setRootDocument( doc );
00493 }
00494 
00495 void KisSelectionManager::selectAll()
00496 {
00497     KisImageSP img = m_parent->currentImg();
00498     if (!img) return;
00499 
00500     KisPaintDeviceSP dev = img->activeDevice();
00501     if (!dev) return;
00502     
00503     KisSelectedTransaction * t = 0;
00504     if (img->undo()) t = new KisSelectedTransaction(i18n("Select &All"), dev);
00505     Q_CHECK_PTR(t);
00506 
00507     dev->selection()->clear();
00508     dev->selection()->invert();
00509     dev->emitSelectionChanged();
00510 
00511     if (img->undo())
00512         img->undoAdapter()->addCommand(t);
00513 }
00514 
00515 void KisSelectionManager::deselect()
00516 {
00517     KisImageSP img = m_parent->currentImg();
00518     if (!img) return;
00519 
00520     KisPaintDeviceSP dev = img->activeDevice();
00521     if (!dev) return;
00522     KisSelectedTransaction * t = 0;
00523     if (img->undo()) t = new KisSelectedTransaction(i18n("Deselect"), dev);
00524     Q_CHECK_PTR(t);
00525 
00526     dev->deselect();
00527     dev->emitSelectionChanged();
00528 
00529     if (img->undo())
00530         img->undoAdapter()->addCommand(t);
00531 }
00532 
00533 
00534 void KisSelectionManager::clear()
00535 {
00536     KisImageSP img = m_parent->currentImg();
00537     if (!img) return;
00538 
00539     KisPaintDeviceSP dev = img->activeDevice();
00540     if (!dev) return;
00541 
00542     if (!dev->hasSelection()) return;
00543 
00544     KisTransaction * t = 0;
00545 
00546     if (img->undo()) {
00547         t = new KisTransaction(i18n("Clear"), dev);
00548     }
00549 
00550     dev->clearSelection();
00551     dev->emitSelectionChanged();
00552 
00553     if (img->undo()) img->undoAdapter()->addCommand(t);
00554 }
00555 
00556 void KisSelectionManager::fill(const KisColor& color, bool fillWithPattern, const QString& transactionText)
00557 {
00558     KisImageSP img = m_parent->currentImg();
00559     if (!img) return;
00560 
00561     KisPaintDeviceSP dev = img->activeDevice();
00562     if (!dev) return;
00563 
00564     if (!dev->hasSelection()) return;
00565 
00566     KisSelectionSP selection = dev->selection();
00567 
00568     KisPaintDeviceSP filled = new KisPaintDevice(dev->colorSpace());
00569     KisFillPainter painter(filled);
00570 
00571     if (fillWithPattern) {
00572         painter.fillRect(0, 0, img->width(), img->height(),
00573                          m_parent->currentPattern());
00574     } else {
00575         painter.fillRect(0, 0, img->width(), img->height(), color);
00576     }
00577 
00578     painter.end();
00579 
00580     KisPainter painter2(dev);
00581 
00582     if (img->undo()) painter2.beginTransaction(transactionText);
00583     painter2.bltSelection(0, 0, COMPOSITE_OVER, filled, OPACITY_OPAQUE,
00584                           0, 0, img->width(), img->height());
00585 
00586     dev->emitSelectionChanged();
00587 
00588     if (img->undo()) {
00589         img->undoAdapter()->addCommand(painter2.endTransaction());
00590     }
00591 }
00592 
00593 void KisSelectionManager::fillForegroundColor()
00594 {
00595     fill(m_parent->fgColor(), false, i18n("Fill with Foreground Color"));
00596 }
00597 
00598 void KisSelectionManager::fillBackgroundColor()
00599 {
00600     fill(m_parent->bgColor(), false, i18n("Fill with Background Color"));
00601 }
00602 
00603 void KisSelectionManager::fillPattern()
00604 {
00605     fill(KisColor(), true, i18n("Fill with Pattern"));
00606 }
00607 
00608 void KisSelectionManager::reselect()
00609 {
00610     KisImageSP img = m_parent->currentImg();
00611     if (!img) return;
00612 
00613     KisPaintDeviceSP dev = img ->activeDevice();
00614     if (!dev) return;
00615 
00616     KisSelectedTransaction * t = 0;
00617     if (img->undo()) t = new KisSelectedTransaction(i18n("&Reselect"), dev);
00618     Q_CHECK_PTR(t);
00619 
00620     dev->reselect(); // sets hasSelection=true
00621     dev->emitSelectionChanged();
00622 
00623     if (img->undo())
00624         img->undoAdapter()->addCommand(t);
00625 }
00626 
00627 
00628 void KisSelectionManager::invert()
00629 {
00630     KisImageSP img = m_parent->currentImg();
00631     if (!img) return;
00632 
00633     KisPaintDeviceSP dev = img->activeDevice();
00634     if (!dev) return;
00635 
00636     if (dev->hasSelection()) {
00637         KisSelectionSP s = dev->selection();
00638 
00639         KisSelectedTransaction * t = 0;
00640         if (img->undo())
00641         {
00642             t = new KisSelectedTransaction(i18n("&Invert"), dev);
00643             Q_CHECK_PTR(t);
00644         }
00645 
00646         s->invert();
00647         dev->emitSelectionChanged();
00648 
00649         if (t) {
00650             img->undoAdapter()->addCommand(t);
00651         }
00652     }
00653 }
00654 
00655 void KisSelectionManager::copySelectionToNewLayer()
00656 {
00657     KisImageSP img = m_parent->currentImg();
00658     if (!img) return;
00659 
00660     KisPaintDeviceSP dev = img->activeDevice();
00661     if (!dev) return;
00662 
00663     copy();
00664     paste();
00665 }
00666 
00667 void KisSelectionManager::cutToNewLayer()
00668 {
00669     KisImageSP img = m_parent->currentImg();
00670     if (!img) return;
00671 
00672     KisPaintDeviceSP dev = img->activeDevice();
00673     if (!dev) return;
00674 
00675     cut();
00676     paste();
00677 }
00678 
00679 
00680 void KisSelectionManager::feather()
00681 {
00682     KisImageSP img = m_parent->currentImg();
00683     if (!img) return;
00684     KisPaintDeviceSP dev = img->activeDevice();
00685     if (!dev) return;
00686 
00687     if (!dev->hasSelection()) {
00688         // activate it, but don't do anything with it
00689         dev->selection();
00690         return;
00691     }
00692 
00693     KisSelectionSP selection = dev->selection();
00694     KisSelectedTransaction * t = 0;
00695     if (img->undo()) t = new KisSelectedTransaction(i18n("Feather..."), dev);
00696     Q_CHECK_PTR(t);
00697 
00698 
00699     // XXX: we should let gaussian blur & others influence alpha channels as well
00700     // (on demand of the caller)
00701 
00702     KisConvolutionPainter painter(selection.data());
00703 
00704     KisKernelSP k = new KisKernel();
00705     k->width = 3;
00706     k->height = 3;
00707     k->factor = 16;
00708     k->offset = 0;
00709     k->data = new Q_INT32[9];
00710     k->data[0] = 1;
00711     k->data[1] = 2;
00712     k->data[2] = 1;
00713     k->data[3] = 2;
00714     k->data[4] = 4;
00715     k->data[5] = 2;
00716     k->data[6] = 1;
00717     k->data[7] = 2;
00718     k->data[8] = 1;
00719 
00720     QRect rect = selection->extent();
00721     // Make sure we've got enough space around the edges.
00722     rect = QRect(rect.x() - 3, rect.y() - 3, rect.width() + 6, rect.height() + 6);
00723     rect &= QRect(0, 0, img->width(), img->height());
00724 
00725     painter.applyMatrix(k, rect.x(), rect.y(), rect.width(), rect.height(), BORDER_AVOID, KisChannelInfo::FLAG_ALPHA);
00726     painter.end();
00727 
00728     dev->emitSelectionChanged();
00729 
00730     if (img->undo())
00731         img->undoAdapter()->addCommand(t);
00732 
00733 }
00734 
00735 void KisSelectionManager::toggleDisplaySelection()
00736 {
00737     m_parent->selectionDisplayToggled(displaySelection());
00738 }
00739 
00740 bool KisSelectionManager::displaySelection()
00741 {
00742     return m_toggleDisplaySelection->isChecked();
00743 }
00744 // XXX: Maybe move these esoteric functions to plugins?
00745 void KisSelectionManager::border() {}
00746 void KisSelectionManager::expand() {}
00747 void KisSelectionManager::contract() {}
00748 void KisSelectionManager::grow() { grow(5, 5); /*shrink(5, 5, true)*/ }
00749 void KisSelectionManager::similar() {}
00750 void KisSelectionManager::transform() {}
00751 void KisSelectionManager::load() {}
00752 void KisSelectionManager::save() {}
00753 
00754 #define MAX(a, b) ((a) > (b) ? (a) : (b))
00755 #define MIN(a, b) ((a) < (b) ? (a) : (b))
00756 
00757 void KisSelectionManager::grow (Q_INT32 xradius, Q_INT32 yradius)
00758 {
00759     KisImageSP img = m_parent->currentImg();
00760     if (!img) return;
00761 
00762     KisPaintDeviceSP dev = img->activeDevice();
00763     if (!dev) return;
00764 
00765     if (!dev->hasSelection()) return;
00766     KisSelectionSP selection = dev->selection();
00767 
00768     //determine the layerSize
00769     QRect layerSize = dev->exactBounds();
00770     /*
00771         Any bugs in this fuction are probably also in thin_region
00772         Blame all bugs in this function on jaycox@gimp.org
00773     */
00774 
00775     Q_UINT8  **buf;  // caches the region's pixel data
00776     Q_UINT8  **max;  // caches the largest values for each column
00777 
00778     if (xradius <= 0 || yradius <= 0)
00779         return;
00780 
00781     KisSelectedTransaction *t = 0;
00782 
00783     if (img->undo()) {
00784         t = new KisSelectedTransaction(i18n("Grow"), dev);
00785         Q_CHECK_PTR(t);
00786     }
00787 
00788     max = new Q_UINT8* [layerSize.width() + 2 * xradius];
00789     buf = new Q_UINT8* [yradius + 1];
00790     for (Q_INT32 i = 0; i < yradius + 1; i++)
00791     {
00792         buf[i] = new Q_UINT8[layerSize.width()];
00793     }
00794     Q_UINT8* buffer = new Q_UINT8[ ( layerSize.width() + 2 * xradius ) * ( yradius + 1 ) ];
00795     for (Q_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
00796     {
00797         if (i < xradius)
00798             max[i] = buffer;
00799         else if (i < layerSize.width() + xradius)
00800             max[i] = &buffer[(yradius + 1) * (i - xradius)];
00801         else
00802             max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
00803 
00804         for (Q_INT32 j = 0; j < xradius + 1; j++)
00805             max[i][j] = 0;
00806     }
00807     /* offset the max pointer by xradius so the range of the array
00808         is [-xradius] to [region->w + xradius] */
00809     max += xradius;
00810 
00811     Q_UINT8* out = new Q_UINT8[ layerSize.width() ]; // holds the new scan line we are computing
00812 
00813     Q_INT32* circ = new Q_INT32[ 2 * xradius + 1 ]; // holds the y coords of the filter's mask
00814     computeBorder (circ, xradius, yradius);
00815 
00816     /* offset the circ pointer by xradius so the range of the array
00817         is [-xradius] to [xradius] */
00818     circ += xradius;
00819 
00820     memset (buf[0], 0, layerSize.width());
00821     for (Q_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
00822     {
00823         selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
00824     }
00825 
00826     for (Q_INT32 x = 0; x < layerSize.width() ; x++) // set up max for top of image
00827     {
00828             max[x][0] = 0;         // buf[0][x] is always 0
00829             max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
00830             for (Q_INT32 j = 2; j < yradius + 1; j++)
00831             {
00832                 max[x][j] = MAX(buf[j][x], max[x][j-1]);
00833             }
00834     }
00835 
00836     for (Q_INT32 y = 0; y < layerSize.height(); y++)
00837     {
00838         rotatePointers (buf, yradius + 1);
00839         if (y < layerSize.height() - (yradius))
00840             selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
00841         else
00842             memset (buf[yradius], 0, layerSize.width());
00843     for (Q_INT32 x = 0; x < layerSize.width(); x++) /* update max array */
00844     {
00845         for (Q_INT32 i = yradius; i > 0; i--)
00846         {
00847             max[x][i] = MAX (MAX (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
00848         }
00849         max[x][0] = buf[0][x];
00850     }
00851     Q_INT32 last_max = max[0][circ[-1]];
00852     Q_INT32 last_index = 1;
00853     for (Q_INT32 x = 0; x < layerSize.width(); x++) /* render scan line */
00854     {
00855         last_index--;
00856         if (last_index >= 0)
00857         {
00858             if (last_max == 255)
00859                 out[x] = 255;
00860             else
00861             {
00862                 last_max = 0;
00863                 for (Q_INT32 i = xradius; i >= 0; i--)
00864                     if (last_max < max[x + i][circ[i]])
00865                     {
00866                         last_max = max[x + i][circ[i]];
00867                         last_index = i;
00868                     }
00869                 out[x] = last_max;
00870             }
00871         }
00872         else
00873         {
00874             last_index = xradius;
00875             last_max = max[x + xradius][circ[xradius]];
00876             for (Q_INT32 i = xradius - 1; i >= -xradius; i--)
00877                 if (last_max < max[x + i][circ[i]])
00878                 {
00879                     last_max = max[x + i][circ[i]];
00880                     last_index = i;
00881                 }
00882             out[x] = last_max;
00883         }
00884     }
00885     selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
00886     }
00887     /* undo the offsets to the pointers so we can free the malloced memmory */
00888     circ -= xradius;
00889     max -= xradius;
00890     //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
00891     delete[] circ;
00892     delete[] buffer;
00893     delete[] max;
00894     for (Q_INT32 i = 0; i < yradius + 1; i++)
00895         delete[] buf[i];
00896     delete[] buf;
00897     delete[] out;
00898 
00899     dev->emitSelectionChanged();
00900 
00901     if (t) {
00902         img->undoAdapter()->addCommand(t);
00903     }
00904 }
00905 
00906 void KisSelectionManager::shrink (Q_INT32 xradius, Q_INT32 yradius, bool edge_lock)
00907 {
00908 
00909     KisImageSP img = m_parent->currentImg();
00910     if (!img) return;
00911 
00912     KisPaintDeviceSP dev = img->activeDevice();
00913     if (!dev) return;
00914 
00915     if (!dev->hasSelection()) return;
00916     KisSelectionSP selection = dev->selection();
00917 
00918     //determine the layerSize
00919     QRect layerSize = dev->exactBounds();
00920   /*
00921      pretty much the same as fatten_region only different
00922      blame all bugs in this function on jaycox@gimp.org
00923   */
00924   /* If edge_lock is true  we assume that pixels outside the region
00925      we are passed are identical to the edge pixels.
00926      If edge_lock is false, we assume that pixels outside the region are 0
00927   */
00928     Q_UINT8  **buf;  // caches the the region's pixels
00929     Q_UINT8  **max;  // caches the smallest values for each column
00930     Q_INT32    last_max, last_index;
00931 
00932     if (xradius <= 0 || yradius <= 0)
00933         return;
00934 
00935     max = new Q_UINT8* [layerSize.width() + 2 * xradius];
00936     buf = new Q_UINT8* [yradius + 1];
00937     for (Q_INT32 i = 0; i < yradius + 1; i++)
00938     {
00939         buf[i] = new Q_UINT8[layerSize.width()];
00940     }
00941 
00942     Q_INT32 buffer_size = (layerSize.width() + 2 * xradius + 1) * (yradius + 1);
00943     Q_UINT8* buffer = new Q_UINT8[buffer_size];
00944 
00945     if (edge_lock)
00946         memset(buffer, 255, buffer_size);
00947     else
00948         memset(buffer, 0, buffer_size);
00949 
00950     for (Q_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
00951     {
00952         if (i < xradius)
00953             if (edge_lock)
00954                 max[i] = buffer;
00955             else
00956                 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
00957         else if (i < layerSize.width() + xradius)
00958             max[i] = &buffer[(yradius + 1) * (i - xradius)];
00959         else
00960             if (edge_lock)
00961                 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
00962             else
00963                 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
00964     }
00965     if (!edge_lock)
00966         for (Q_INT32 j = 0 ; j < xradius + 1; j++) max[0][j] = 0;
00967 
00968     // offset the max pointer by xradius so the range of the array is [-xradius] to [region->w + xradius]
00969     max += xradius;
00970 
00971     Q_UINT8* out = new Q_UINT8[layerSize.width()]; // holds the new scan line we are computing
00972 
00973     Q_INT32* circ = new Q_INT32[2 * xradius + 1]; // holds the y coords of the filter's mask
00974 
00975     computeBorder (circ, xradius, yradius);
00976 
00977     // offset the circ pointer by xradius so the range of the array is [-xradius] to [xradius]
00978     circ += xradius;
00979 
00980     for (Q_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
00981         selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
00982 
00983     if (edge_lock)
00984         memcpy (buf[0], buf[1], layerSize.width());
00985     else
00986         memset (buf[0], 0, layerSize.width());
00987 
00988 
00989     for (Q_INT32 x = 0; x < layerSize.width(); x++) // set up max for top of image
00990     {
00991         max[x][0] = buf[0][x];
00992         for (Q_INT32 j = 1; j < yradius + 1; j++)
00993             max[x][j] = MIN(buf[j][x], max[x][j-1]);
00994     }
00995 
00996     for (Q_INT32 y = 0; y < layerSize.height(); y++)
00997     {
00998         rotatePointers (buf, yradius + 1);
00999         if (y < layerSize.height() - yradius)
01000             selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
01001         else if (edge_lock)
01002             memcpy (buf[yradius], buf[yradius - 1], layerSize.width());
01003         else
01004             memset (buf[yradius], 0, layerSize.width());
01005 
01006         for (Q_INT32 x = 0 ; x < layerSize.width(); x++) // update max array
01007         {
01008             for (Q_INT32 i = yradius; i > 0; i--)
01009             {
01010                 max[x][i] = MIN (MIN (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
01011             }
01012             max[x][0] = buf[0][x];
01013         }
01014         last_max =  max[0][circ[-1]];
01015         last_index = 0;
01016 
01017         for (Q_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
01018         {
01019             last_index--;
01020             if (last_index >= 0)
01021             {
01022                 if (last_max == 0)
01023                 out[x] = 0;
01024                 else
01025                 {
01026                     last_max = 255;
01027                     for (Q_INT32 i = xradius; i >= 0; i--)
01028                         if (last_max > max[x + i][circ[i]])
01029                         {
01030                             last_max = max[x + i][circ[i]];
01031                             last_index = i;
01032                         }
01033                     out[x] = last_max;
01034                 }
01035             }
01036             else
01037             {
01038                 last_index = xradius;
01039                 last_max = max[x + xradius][circ[xradius]];
01040                 for (Q_INT32 i = xradius - 1; i >= -xradius; i--)
01041                 if (last_max > max[x + i][circ[i]])
01042                     {
01043                     last_max = max[x + i][circ[i]];
01044                     last_index = i;
01045                     }
01046                 out[x] = last_max;
01047             }
01048         }
01049         selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
01050     }
01051 
01052     // undo the offsets to the pointers so we can free the malloced memmory
01053     circ -= xradius;
01054     max -= xradius;
01055     //free the memmory
01056     //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
01057     delete[] circ;
01058     delete[] buffer;
01059     delete[] max;
01060     for (Q_INT32 i = 0; i < yradius + 1; i++)
01061             delete buf[i];
01062     delete[] buf;
01063     delete[] out;
01064 
01065     dev->emitSelectionChanged();
01066 }
01067 
01068 //Simple convolution filter to smooth a mask (1bpp)
01069 
01070 void KisSelectionManager::smooth()
01071 {
01072     KisImageSP img = m_parent->currentImg();
01073     if (!img) return;
01074 
01075     KisPaintDeviceSP dev = img->activeDevice();
01076     if (!dev) return;
01077 
01078     if (!dev->hasSelection()) return;
01079     KisSelectionSP selection = dev->selection();
01080 
01081     //determine the layerSize
01082     QRect layerSize = dev->exactBounds();
01083 
01084     Q_UINT8      *buf[3];
01085 
01086     Q_INT32 width = layerSize.width();
01087 
01088     for (Q_INT32 i = 0; i < 3; i++) buf[i] = new Q_UINT8[width + 2];
01089 
01090     Q_UINT8* out = new Q_UINT8[width];
01091 
01092     // load top of image
01093     selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
01094 
01095     buf[0][0]         = buf[0][1];
01096     buf[0][width + 1] = buf[0][width];
01097 
01098     memcpy (buf[1], buf[0], width + 2);
01099 
01100     for (Q_INT32 y = 0; y < layerSize.height(); y++)
01101     {
01102         if (y + 1 < layerSize.height())
01103         {
01104             selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
01105 
01106             buf[2][0]         = buf[2][1];
01107             buf[2][width + 1] = buf[2][width];
01108         }
01109         else
01110         {
01111             memcpy (buf[2], buf[1], width + 2);
01112         }
01113 
01114         for (Q_INT32 x = 0 ; x < width; x++)
01115         {
01116             Q_INT32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
01117                              buf[1][x] + buf[2][x+1] + buf[1][x+2] +
01118                              buf[2][x] + buf[1][x+1] + buf[2][x+2]);
01119 
01120             out[x] = value / 9;
01121         }
01122 
01123         selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
01124 
01125         rotatePointers (buf, 3);
01126     }
01127 
01128     for (Q_INT32 i = 0; i < 3; i++)
01129         delete[] buf[i];
01130 
01131     delete[] out;
01132 
01133     dev->emitSelectionChanged();
01134 }
01135 
01136 // Erode (radius 1 pixel) a mask (1bpp)
01137 
01138 void KisSelectionManager::erode()
01139 {
01140     KisImageSP img = m_parent->currentImg();
01141     if (!img) return;
01142 
01143     KisPaintDeviceSP dev = img->activeDevice();
01144     if (!dev) return;
01145 
01146     if (!dev->hasSelection()) return;
01147     KisSelectionSP selection = dev->selection();
01148 
01149     //determine the layerSize
01150     QRect layerSize = dev->exactBounds();
01151 
01152     Q_UINT8* buf[3];
01153 
01154 
01155     Q_INT32 width = layerSize.width();
01156 
01157     for (Q_INT32 i = 0; i < 3; i++)
01158         buf[i] = new Q_UINT8[width + 2];
01159 
01160     Q_UINT8* out = new Q_UINT8[width];
01161 
01162     // load top of image
01163     selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
01164 
01165     buf[0][0]         = buf[0][1];
01166     buf[0][width + 1] = buf[0][width];
01167 
01168     memcpy (buf[1], buf[0], width + 2);
01169 
01170     for (Q_INT32 y = 0; y < layerSize.height(); y++)
01171     {
01172         if (y + 1 < layerSize.height())
01173         {
01174             selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
01175 
01176             buf[2][0]         = buf[2][1];
01177             buf[2][width + 1] = buf[2][width];
01178         }
01179         else
01180         {
01181             memcpy (buf[2], buf[1], width + 2);
01182         }
01183 
01184       for (Q_INT32 x = 0 ; x < width; x++)
01185         {
01186           Q_INT32 min = 255;
01187 
01188           if (buf[0][x+1] < min) min = buf[0][x+1];
01189           if (buf[1][x]   < min) min = buf[1][x];
01190           if (buf[1][x+1] < min) min = buf[1][x+1];
01191           if (buf[1][x+2] < min) min = buf[1][x+2];
01192           if (buf[2][x+1] < min) min = buf[2][x+1];
01193 
01194           out[x] = min;
01195         }
01196 
01197         selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
01198 
01199         rotatePointers (buf, 3);
01200     }
01201 
01202     for (Q_INT32 i = 0; i < 3; i++)
01203         delete[] buf[i];
01204 
01205     delete[] out;
01206 
01207     dev->emitSelectionChanged();
01208 }
01209 
01210 // dilate (radius 1 pixel) a mask (1bpp)
01211 
01212 void KisSelectionManager::dilate()
01213 {
01214     KisImageSP img = m_parent->currentImg();
01215     if (!img) return;
01216 
01217     KisPaintDeviceSP dev = img->activeDevice();
01218     if (!dev) return;
01219 
01220     if (!dev->hasSelection()) return;
01221     KisSelectionSP selection = dev->selection();
01222 
01223     //determine the layerSize
01224     QRect layerSize = dev->exactBounds();
01225 
01226     Q_UINT8* buf[3];
01227 
01228     Q_INT32 width = layerSize.width();
01229 
01230     for (Q_INT32 i = 0; i < 3; i++)
01231         buf[i] = new Q_UINT8[width + 2];
01232 
01233     Q_UINT8* out = new Q_UINT8[width];
01234 
01235     // load top of image
01236     selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
01237 
01238     buf[0][0]         = buf[0][1];
01239     buf[0][width + 1] = buf[0][width];
01240 
01241     memcpy (buf[1], buf[0], width + 2);
01242 
01243     for (Q_INT32 y = 0; y < layerSize.height(); y++)
01244     {
01245         if (y + 1 < layerSize.height())
01246         {
01247             selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
01248 
01249             buf[2][0]         = buf[2][1];
01250             buf[2][width + 1] = buf[2][width];
01251         }
01252         else
01253         {
01254             memcpy (buf[2], buf[1], width + 2);
01255         }
01256 
01257         for (Q_INT32 x = 0 ; x < width; x++)
01258         {
01259             Q_INT32 max = 0;
01260 
01261             if (buf[0][x+1] > max) max = buf[0][x+1];
01262             if (buf[1][x]   > max) max = buf[1][x];
01263             if (buf[1][x+1] > max) max = buf[1][x+1];
01264             if (buf[1][x+2] > max) max = buf[1][x+2];
01265             if (buf[2][x+1] > max) max = buf[2][x+1];
01266 
01267             out[x] = max;
01268         }
01269 
01270         selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
01271 
01272         rotatePointers (buf, 3);
01273     }
01274 
01275     for (Q_INT32 i = 0; i < 3; i++)
01276         delete[] buf[i];
01277 
01278     delete[] out;
01279 
01280     dev->emitSelectionChanged();
01281 }
01282 
01283 void KisSelectionManager::border(Q_INT32 xradius, Q_INT32 yradius)
01284 {
01285     KisImageSP img = m_parent->currentImg();
01286     if (!img) return;
01287 
01288     KisPaintDeviceSP dev = img->activeDevice();
01289     if (!dev) return;
01290 
01291     if (!dev->hasSelection()) return;
01292     KisSelectionSP selection = dev->selection();
01293 
01294     //determine the layerSize
01295     QRect layerSize = dev->exactBounds();
01296 
01297   /*
01298      This function has no bugs, but if you imagine some you can
01299      blame them on jaycox@gimp.org
01300   */
01301     Q_UINT8  *buf[3];
01302     Q_UINT8 **density;
01303     Q_UINT8 **transition;
01304 
01305     if (xradius == 1 && yradius == 1) // optimize this case specifically
01306     {
01307         Q_UINT8* source[3];
01308 
01309         for (Q_INT32 i = 0; i < 3; i++)
01310             source[i] = new Q_UINT8[layerSize.width()];
01311 
01312         Q_UINT8* transition = new Q_UINT8[layerSize.width()];
01313 
01314         selection->readBytes(source[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
01315         memcpy (source[1], source[0], layerSize.width());
01316         if (layerSize.height() > 1)
01317             selection->readBytes(source[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
01318         else
01319             memcpy (source[2], source[1], layerSize.width());
01320 
01321         computeTransition (transition, source, layerSize.width());
01322         selection->writeBytes(transition, layerSize.x(), layerSize.y(), layerSize.width(), 1);
01323 
01324         for (Q_INT32 y = 1; y < layerSize.height(); y++)
01325         {
01326             rotatePointers (source, 3);
01327             if (y + 1 < layerSize.height())
01328                 selection->readBytes(source[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
01329             else
01330                 memcpy(source[2], source[1], layerSize.width());
01331             computeTransition (transition, source, layerSize.width());
01332             selection->writeBytes(transition, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
01333         }
01334 
01335         for (Q_INT32 i = 0; i < 3; i++)
01336             delete[] source[i];
01337         delete[] transition;
01338         return;
01339     }
01340 
01341     Q_INT32* max = new Q_INT32[layerSize.width() + 2 * xradius];
01342     for (Q_INT32 i = 0; i < (layerSize.width() + 2 * xradius); i++)
01343         max[i] = yradius + 2;
01344     max += xradius;
01345 
01346     for (Q_INT32 i = 0; i < 3; i++)
01347         buf[i] = new Q_UINT8[layerSize.width()];
01348 
01349     transition = new Q_UINT8*[yradius + 1];
01350     for (Q_INT32 i = 0; i < yradius + 1; i++)
01351     {
01352         transition[i] = new Q_UINT8[layerSize.width() + 2 * xradius];
01353         memset(transition[i], 0, layerSize.width() + 2 * xradius);
01354         transition[i] += xradius;
01355     }
01356     Q_UINT8* out = new Q_UINT8[layerSize.width()];
01357     density = new Q_UINT8*[2 * xradius + 1];
01358     density += xradius;
01359 
01360     for (Q_INT32 x = 0; x < (xradius + 1); x++) // allocate density[][]
01361     {
01362         density[ x]  = new Q_UINT8[2 * yradius + 1];
01363         density[ x] += yradius;
01364         density[-x]  = density[x];
01365     }
01366     for (Q_INT32 x = 0; x < (xradius + 1); x++) // compute density[][]
01367     {
01368         double tmpx, tmpy, dist;
01369         Q_UINT8 a;
01370 
01371         if (x > 0)
01372             tmpx = x - 0.5;
01373         else if (x < 0)
01374             tmpx = x + 0.5;
01375         else
01376             tmpx = 0.0;
01377 
01378         for (Q_INT32 y = 0; y < (yradius + 1); y++)
01379         {
01380             if (y > 0)
01381                 tmpy = y - 0.5;
01382             else if (y < 0)
01383                 tmpy = y + 0.5;
01384             else
01385                 tmpy = 0.0;
01386             dist = ((tmpy * tmpy) / (yradius * yradius) +
01387                     (tmpx * tmpx) / (xradius * xradius));
01388             if (dist < 1.0)
01389                 a = 255 * (Q_UINT8)(1.0 - sqrt (dist));
01390             else
01391                 a = 0;
01392             density[ x][ y] = a;
01393             density[ x][-y] = a;
01394             density[-x][ y] = a;
01395             density[-x][-y] = a;
01396         }
01397     }
01398     selection->readBytes(buf[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
01399     memcpy (buf[1], buf[0], layerSize.width());
01400     if (layerSize.height() > 1)
01401         selection->readBytes(buf[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
01402     else
01403         memcpy (buf[2], buf[1], layerSize.width());
01404     computeTransition (transition[1], buf, layerSize.width());
01405 
01406     for (Q_INT32 y = 1; y < yradius && y + 1 < layerSize.height(); y++) // set up top of image
01407     {
01408         rotatePointers (buf, 3);
01409         selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
01410         computeTransition (transition[y + 1], buf, layerSize.width());
01411     }
01412     for (Q_INT32 x = 0; x < layerSize.width(); x++) // set up max[] for top of image
01413     {
01414         max[x] = -(yradius + 7);
01415         for (Q_INT32 j = 1; j < yradius + 1; j++)
01416             if (transition[j][x])
01417             {
01418                 max[x] = j;
01419                 break;
01420             }
01421     }
01422     for (Q_INT32 y = 0; y < layerSize.height(); y++) // main calculation loop
01423     {
01424         rotatePointers (buf, 3);
01425         rotatePointers (transition, yradius + 1);
01426         if (y < layerSize.height() - (yradius + 1))
01427         {
01428             selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + yradius + 1, layerSize.width(), 1);
01429             computeTransition (transition[yradius], buf, layerSize.width());
01430         }
01431         else
01432             memcpy (transition[yradius], transition[yradius - 1], layerSize.width());
01433 
01434         for (Q_INT32 x = 0; x < layerSize.width(); x++) // update max array
01435         {
01436             if (max[x] < 1)
01437             {
01438                 if (max[x] <= -yradius)
01439                 {
01440                     if (transition[yradius][x])
01441                         max[x] = yradius;
01442                     else
01443                         max[x]--;
01444                 }
01445                 else
01446                     if (transition[-max[x]][x])
01447                         max[x] = -max[x];
01448                     else if (transition[-max[x] + 1][x])
01449                         max[x] = -max[x] + 1;
01450                 else
01451                   max[x]--;
01452             }
01453             else
01454                 max[x]--;
01455             if (max[x] < -yradius - 1)
01456                 max[x] = -yradius - 1;
01457         }
01458         Q_UINT8 last_max =  max[0][density[-1]];
01459         Q_INT32 last_index = 1;
01460         for (Q_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
01461         {
01462             last_index--;
01463             if (last_index >= 0)
01464             {
01465                 last_max = 0;
01466                 for (Q_INT32 i = xradius; i >= 0; i--)
01467                     if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x+i]] > last_max)
01468                     {
01469                         last_max = density[i][max[x + i]];
01470                         last_index = i;
01471                     }
01472                 out[x] = last_max;
01473             }
01474             else
01475             {
01476                 last_max = 0;
01477                 for (Q_INT32 i = xradius; i >= -xradius; i--)
01478                     if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x + i]] > last_max)
01479                     {
01480                         last_max = density[i][max[x + i]];
01481                         last_index = i;
01482                     }
01483                 out[x] = last_max;
01484             }
01485             if (last_max == 0)
01486             {
01487                 Q_INT32 i;
01488                 for (i = x + 1; i < layerSize.width(); i++)
01489                 {
01490                     if (max[i] >= -yradius)
01491                         break;
01492                 }
01493                 if (i - x > xradius)
01494                 {
01495                     for (; x < i - xradius; x++)
01496                         out[x] = 0;
01497                     x--;
01498                 }
01499                 last_index = xradius;
01500             }
01501         }
01502         selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
01503     }
01504     delete [] out;
01505 
01506     for (Q_INT32 i = 0; i < 3; i++)
01507         delete buf[i];
01508 
01509     max -= xradius;
01510     delete[] max;
01511 
01512     for (Q_INT32 i = 0; i < yradius + 1; i++)
01513     {
01514         transition[i] -= xradius;
01515         delete transition[i];
01516     }
01517     delete[] transition;
01518 
01519     for (Q_INT32 i = 0; i < xradius + 1 ; i++)
01520     {
01521         density[i] -= yradius;
01522         delete density[i];
01523     }
01524     density -= xradius;
01525     delete[] density;
01526 
01527     dev->emitSelectionChanged();
01528 }
01529 
01530 #define RINT(x) floor ((x) + 0.5)
01531 
01532 void KisSelectionManager::computeBorder (Q_INT32  *circ, Q_INT32  xradius, Q_INT32  yradius)
01533 {
01534     Q_ASSERT(xradius != 0);
01535     Q_INT32 i;
01536     Q_INT32 diameter = xradius * 2 + 1;
01537     double tmp;
01538 
01539     for (i = 0; i < diameter; i++)
01540     {
01541         if (i > xradius)
01542         tmp = (i - xradius) - 0.5;
01543         else if (i < xradius)
01544         tmp = (xradius - i) - 0.5;
01545         else
01546         tmp = 0.0;
01547 
01548         circ[i] = (Q_INT32) RINT (yradius / (double) xradius * sqrt (xradius * xradius - tmp * tmp));
01549     }
01550 }
01551 
01552 void KisSelectionManager::rotatePointers (Q_UINT8  **p, Q_UINT32 n)
01553 {
01554     Q_UINT32  i;
01555     Q_UINT8  *tmp;
01556 
01557     tmp = p[0];
01558 
01559     for (i = 0; i < n - 1; i++) p[i] = p[i + 1];
01560 
01561     p[i] = tmp;
01562 }
01563 
01564 void KisSelectionManager::computeTransition (Q_UINT8* transition, Q_UINT8** buf, Q_INT32 width)
01565 {
01566     Q_INT32 x = 0;
01567 
01568     if (width == 1)
01569     {
01570         if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
01571             transition[x] = 255;
01572         else
01573             transition[x] = 0;
01574     return;
01575     }
01576     if (buf[1][x] > 127)
01577     {
01578         if ( buf[0][x] < 128 || buf[0][x + 1] < 128 ||
01579             buf[1][x + 1] < 128 ||
01580             buf[2][x] < 128 || buf[2][x + 1] < 128 )
01581             transition[x] = 255;
01582         else
01583             transition[x] = 0;
01584     }
01585     else
01586         transition[x] = 0;
01587     for (Q_INT32 x = 1; x < width - 1; x++)
01588     {
01589         if (buf[1][x] >= 128)
01590         {
01591             if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
01592                 buf[1][x - 1] < 128           ||          buf[1][x + 1] < 128 ||
01593                 buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
01594                 transition[x] = 255;
01595             else
01596                 transition[x] = 0;
01597         }
01598         else
01599             transition[x] = 0;
01600     }
01601     if (buf[1][x] >= 128)
01602     {
01603         if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
01604             buf[1][x - 1] < 128 ||
01605             buf[2][x - 1] < 128 || buf[2][x] < 128)
01606             transition[x] = 255;
01607         else
01608             transition[x] = 0;
01609     }
01610     else
01611         transition[x] = 0;
01612 }
01613 
01614 #include "kis_selection_manager.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys