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