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