summaryrefslogtreecommitdiffstats
path: root/chalk/ui/kis_selection_manager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/ui/kis_selection_manager.cpp')
-rw-r--r--chalk/ui/kis_selection_manager.cpp1643
1 files changed, 1643 insertions, 0 deletions
diff --git a/chalk/ui/kis_selection_manager.cpp b/chalk/ui/kis_selection_manager.cpp
new file mode 100644
index 000000000..7d001586a
--- /dev/null
+++ b/chalk/ui/kis_selection_manager.cpp
@@ -0,0 +1,1643 @@
+/*
+ * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <tqobject.h>
+#include <tqapplication.h>
+#include <tqclipboard.h>
+#include <tqcolor.h>
+#include <tqcursor.h>
+
+#include <kdebug.h>
+#include <tdeaction.h>
+#include <tdelocale.h>
+#include <kstdaction.h>
+
+#include <KoDocument.h>
+#include <KoMainWindow.h>
+#include <KoQueryTrader.h>
+
+#include "kis_cursor.h"
+#include "kis_part_layer.h"
+#include "kis_adjustment_layer.h"
+#include "kis_clipboard.h"
+#include "kis_types.h"
+#include "kis_view.h"
+#include "kis_doc.h"
+#include "kis_image.h"
+#include "kis_selection.h"
+#include "kis_selection_manager.h"
+#include "kis_painter.h"
+#include "kis_iterators_pixel.h"
+#include "kis_iteratorpixeltrait.h"
+#include "kis_layer.h"
+#include "kis_group_layer.h"
+#include "kis_paint_layer.h"
+#include "kis_paint_device.h"
+#include "kis_channelinfo.h"
+#include "kis_dlg_apply_profile.h"
+#include "kis_config.h"
+#include "kis_debug_areas.h"
+#include "kis_transaction.h"
+#include "kis_undo_adapter.h"
+#include "kis_selected_transaction.h"
+#include "kis_convolution_painter.h"
+#include "kis_integer_maths.h"
+#include "kis_fill_painter.h"
+#include "kis_canvas.h"
+
+KisSelectionManager::KisSelectionManager(KisView * parent, KisDoc * doc)
+ : m_parent(parent),
+ m_doc(doc),
+ m_copy(0),
+ m_cut(0),
+ m_paste(0),
+ m_pasteNew(0),
+ m_cutToNewLayer(0),
+ m_selectAll(0),
+ m_deselect(0),
+ m_clear(0),
+ m_reselect(0),
+ m_invert(0),
+ m_toNewLayer(0),
+ m_feather(0),
+ m_border(0),
+ m_expand(0),
+ m_smooth(0),
+ m_contract(0),
+ m_similar(0),
+ m_transform(0),
+ m_load(0),
+ m_save(0),
+ m_fillForegroundColor(0),
+ m_fillBackgroundColor(0),
+ m_fillPattern(0)
+{
+ m_pluginActions.setAutoDelete(true);
+ m_clipboard = KisClipboard::instance();
+}
+
+KisSelectionManager::~KisSelectionManager()
+{
+ m_pluginActions.clear();
+}
+
+
+void KisSelectionManager::setup(TDEActionCollection * collection)
+{
+ // XXX: setup shortcuts!
+
+ m_cut = KStdAction::cut(this,
+ TQT_SLOT(cut()),
+ collection,
+ "cut");
+
+ m_copy = KStdAction::copy(this,
+ TQT_SLOT(copy()),
+ collection,
+ "copy");
+
+ m_paste = KStdAction::paste(this,
+ TQT_SLOT(paste()),
+ collection,
+ "paste");
+
+ m_pasteNew = new TDEAction(i18n("Paste into &New Image"),
+ 0, 0,
+ this, TQT_SLOT(pasteNew()),
+ collection,
+ "paste_new");
+
+
+ m_selectAll = KStdAction::selectAll(this,
+ TQT_SLOT(selectAll()),
+ collection,
+ "select_all");
+
+ m_deselect = KStdAction::deselect(this,
+ TQT_SLOT(deselect()),
+ collection,
+ "deselect");
+
+
+ m_clear = KStdAction::clear(this,
+ TQT_SLOT(clear()),
+ collection,
+ "clear");
+
+ m_reselect = new TDEAction(i18n("&Reselect"),
+ 0, "Ctrl+Shift+D",
+ this, TQT_SLOT(reselect()),
+ collection, "reselect");
+
+ m_invert = new TDEAction(i18n("&Invert"),
+ 0, "Ctrl+I",
+ this, TQT_SLOT(invert()),
+ collection, "invert");
+
+
+ m_toNewLayer = new TDEAction(i18n("Copy Selection to New Layer"),
+ 0, "Ctrl+J",
+ this, TQT_SLOT(copySelectionToNewLayer()),
+ collection, "copy_selection_to_new_layer");
+
+
+ m_cutToNewLayer = new TDEAction(i18n("Cut Selection to New Layer"),
+ 0, "Ctrl+Shift+J",
+ this, TQT_SLOT(cutToNewLayer()),
+ collection, "cut_selection_to_new_layer");
+
+ m_feather = new TDEAction(i18n("Feather"),
+ 0, "Ctrl+Alt+D",
+ this, TQT_SLOT(feather()),
+ collection, "feather");
+
+ m_fillForegroundColor = new TDEAction(i18n("Fill with Foreground Color"),
+ "Alt+backspace", this,
+ TQT_SLOT(fillForegroundColor()),
+ collection,
+ "fill_selection_foreground_color");
+ m_fillBackgroundColor = new TDEAction(i18n("Fill with Background Color"),
+ "backspace", this,
+ TQT_SLOT(fillBackgroundColor()),
+ collection,
+ "fill_selection_background_color");
+ m_fillPattern = new TDEAction(i18n("Fill with Pattern"),
+ 0, this,
+ TQT_SLOT(fillPattern()),
+ collection,
+ "fill_selection_pattern");
+
+ m_toggleDisplaySelection = new TDEToggleAction(i18n("Display Selection"), "Ctrl+h", this, TQT_SLOT(toggleDisplaySelection()), collection, "toggle_display_selection");
+ m_toggleDisplaySelection->setCheckedState(KGuiItem(i18n("Hide Selection")));
+ m_toggleDisplaySelection->setChecked(true);
+
+ m_border =
+ new TDEAction(i18n("Border..."),
+ 0, 0,
+ this, TQT_SLOT(border()),
+ collection, "border");
+ m_expand =
+ new TDEAction(i18n("Expand..."),
+ 0, 0,
+ this, TQT_SLOT(expand()),
+ collection, "expand");
+
+ m_smooth =
+ new TDEAction(i18n("Smooth..."),
+ 0, 0,
+ this, TQT_SLOT(smooth()),
+ collection, "smooth");
+
+
+ m_contract =
+ new TDEAction(i18n("Contract..."),
+ 0, 0,
+ this, TQT_SLOT(contract()),
+ collection, "contract");
+ m_similar =
+ new TDEAction(i18n("Similar"),
+ 0, 0,
+ this, TQT_SLOT(similar()),
+ collection, "similar");
+
+
+ m_transform
+ = new TDEAction(i18n("Transform..."),
+ 0, 0,
+ this, TQT_SLOT(transform()),
+ collection, "transform_selection");
+
+
+// m_load
+// = new TDEAction(i18n("Load..."),
+// 0, 0,
+// this, TQT_SLOT(load()),
+// collection, "load_selection");
+//
+//
+// m_save
+// = new TDEAction(i18n("Save As..."),
+// 0, 0,
+// this, TQT_SLOT(save()),
+// collection, "save_selection");
+
+ TQClipboard *cb = TQApplication::clipboard();
+ connect(cb, TQT_SIGNAL(dataChanged()), TQT_SLOT(clipboardDataChanged()));
+}
+
+void KisSelectionManager::clipboardDataChanged()
+{
+ updateGUI();
+}
+
+
+void KisSelectionManager::addSelectionAction(TDEAction * action)
+{
+ m_pluginActions.append(action);
+}
+
+
+void KisSelectionManager::updateGUI()
+{
+ Q_ASSERT(m_parent);
+ Q_ASSERT(m_clipboard);
+
+ if (m_parent == 0) {
+ // "Eek, no parent!
+ return;
+ }
+
+ if (m_clipboard == 0) {
+ // Eek, no clipboard!
+ return;
+ }
+
+ KisImageSP img = m_parent->currentImg();
+ KisLayerSP l = 0;
+ KisPaintDeviceSP dev = 0;
+
+ bool enable = false;
+ if (img && img->activeDevice() && img->activeLayer()) {
+ l = img->activeLayer();
+ dev = img->activeDevice();
+
+
+ KisPartLayer * partLayer = dynamic_cast<KisPartLayer*>(l.data());
+ KisAdjustmentLayer * adjLayer = dynamic_cast<KisAdjustmentLayer*>(l.data());
+
+ enable = l && dev&& dev->hasSelection() && !l->locked() && l->visible() && (partLayer==0);
+
+ if(dev && !adjLayer)
+ m_reselect->setEnabled( dev->selectionDeselected() );
+ if (adjLayer) // There's no reselect for adjustment layers
+ m_reselect->setEnabled(false);
+ }
+
+ m_cut->setEnabled(enable);
+ m_cutToNewLayer->setEnabled(enable);
+ m_selectAll->setEnabled(img != 0);
+ m_deselect->setEnabled(enable);
+ m_clear->setEnabled(enable);
+ m_fillForegroundColor->setEnabled(enable);
+ m_fillBackgroundColor->setEnabled(enable);
+ m_fillPattern->setEnabled(enable);
+ m_invert->setEnabled(enable);
+
+ m_feather->setEnabled(enable);
+
+ m_border->setEnabled(enable);
+ m_expand->setEnabled(enable);
+ m_smooth->setEnabled(enable);
+ m_contract->setEnabled(enable);
+ m_similar->setEnabled(enable);
+ m_transform->setEnabled(enable);
+// m_load->setEnabled(enable);
+// m_save->setEnabled(enable);
+
+
+ TDEAction * a;
+ for (a = m_pluginActions.first(); a; a = m_pluginActions.next()) {
+ a->setEnabled(img != 0);
+ }
+
+ // You can copy from locked layers and paste the clip into a new layer, even when
+ // the current layer is locked.
+ enable = false;
+ if (img && l && dev) {
+ enable = dev->hasSelection() && l->visible();
+ }
+
+ m_copy->setEnabled(enable);
+ m_paste->setEnabled(img != 0 && m_clipboard->hasClip());
+ m_pasteNew->setEnabled(img != 0 && m_clipboard->hasClip());
+ m_toNewLayer->setEnabled(enable);
+
+ m_parent->updateStatusBarSelectionLabel();
+
+}
+
+void KisSelectionManager::imgSelectionChanged(KisImageSP img)
+{
+ if (img == m_parent->currentImg()) {
+ updateGUI();
+ }
+}
+
+void KisSelectionManager::cut()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+
+ copy();
+
+ KisSelectedTransaction *t = 0;
+
+ if (img->undo()) {
+ t = new KisSelectedTransaction(i18n("Cut"), dev);
+ TQ_CHECK_PTR(t);
+ }
+
+ dev->clearSelection();
+ dev->deselect();
+ dev->emitSelectionChanged();
+
+ if (img->undo()) {
+ img->undoAdapter()->addCommand(t);
+ }
+}
+
+void KisSelectionManager::copy()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+
+ KisSelectionSP selection = dev->selection();
+
+ TQRect r = selection->selectedExactRect();
+
+ KisPaintDeviceSP clip = new KisPaintDevice(dev->colorSpace(), "clip");
+ TQ_CHECK_PTR(clip);
+
+ KisColorSpace * cs = clip->colorSpace();
+
+ // TODO if the source is linked... copy from all linked layers?!?
+
+ // Copy image data
+ KisPainter gc;
+ gc.begin(clip);
+ gc.bitBlt(0, 0, COMPOSITE_COPY, dev, r.x(), r.y(), r.width(), r.height());
+ gc.end();
+
+ // Apply selection mask.
+
+ for (TQ_INT32 y = 0; y < r.height(); y++) {
+ KisHLineIteratorPixel layerIt = clip->createHLineIterator(0, y, r.width(), true);
+ KisHLineIteratorPixel selectionIt = selection->createHLineIterator(r.x(), r.y() + y, r.width(), false);
+
+ while (!layerIt.isDone()) {
+
+ cs->applyAlphaU8Mask( layerIt.rawData(), selectionIt.rawData(), 1 );
+
+
+ ++layerIt;
+ ++selectionIt;
+ }
+ }
+
+ m_clipboard->setClip(clip);
+ imgSelectionChanged(m_parent->currentImg());
+}
+
+
+KisLayerSP KisSelectionManager::paste()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return 0;
+
+ KisPaintDeviceSP clip = m_clipboard->clip();
+
+ if (clip) {
+ TQApplication::setOverrideCursor(KisCursor::waitCursor());
+ KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE);
+ TQ_CHECK_PTR(layer);
+
+ TQRect r = clip->exactBounds();
+ KisPainter gc;
+ gc.begin(layer->paintDevice());
+ gc.bitBlt(0, 0, COMPOSITE_COPY, clip, r.x(), r.y(), r.width(), r.height());
+ gc.end();
+
+ //figure out where to position the clip
+ KisCanvasController *cc = m_parent->getCanvasController();
+ TQPoint center = cc->viewToWindow(TQPoint(cc->kiscanvas()->width()/2, cc->kiscanvas()->height()/2));
+ TQPoint bottomright = cc->viewToWindow(TQPoint(cc->kiscanvas()->width(), cc->kiscanvas()->height()));
+ if(bottomright.x() > img->width())
+ center.setX(img->width()/2);
+ if(bottomright.y() > img->height())
+ center.setY(img->height()/2);
+ center -= TQPoint(r.width()/2, r.height()/2);
+ layer->setX(center.x());
+ layer->setY(center.y());
+
+/*XXX CBR have an idea of asking the user if he is about to paste a clip ion another cs than that of
+ the image if that is what he want rather than silently converting
+ if (clip->colorSpace != img ->colorSpace())
+ if (dlg->exec() == TQDialog::Accepted)
+ layer->convertTo(img->colorSpace());
+*/
+ TQApplication::restoreOverrideCursor();
+ if(img->addLayer(layer, img->activeLayer()->parent(), img->activeLayer()))
+ {
+ return layer;
+ } else {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+void KisSelectionManager::pasteNew()
+{
+ KisPaintDeviceSP clip = m_clipboard->clip();
+ if (!clip) return;
+
+ TQRect r = clip->exactBounds();
+ if (r.width() < 1 && r.height() < 1) {
+ // Don't paste empty clips
+ return;
+ }
+
+ const TQCString mimetype = KoDocument::readNativeFormatMimeType();
+ KoDocumentEntry entry = KoDocumentEntry::queryByMimeType( mimetype );
+ KisDoc * doc = (KisDoc*) entry.createDoc();
+
+ Q_ASSERT(doc->undoAdapter() != 0);
+ doc->undoAdapter()->setUndo(false);
+
+ KisImageSP img = new KisImage(doc->undoAdapter(), r.width(), r.height(), clip->colorSpace(), "Pasted");
+ KisPaintLayer *layer = new KisPaintLayer(img, clip->name(), OPACITY_OPAQUE, clip->colorSpace());
+
+ KisPainter p(layer->paintDevice());
+ p.bitBlt(0, 0, COMPOSITE_COPY, clip, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
+ p.end();
+
+ img->addLayer(layer, img->rootLayer(), 0);
+ doc->setCurrentImage(img);
+
+ doc->undoAdapter()->setUndo(true);
+
+ KoMainWindow *win = new KoMainWindow( doc->instance() );
+ win->show();
+ win->setRootDocument( doc );
+}
+
+void KisSelectionManager::selectAll()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ KisSelectedTransaction * t = 0;
+ if (img->undo()) t = new KisSelectedTransaction(i18n("Select All"), dev);
+ TQ_CHECK_PTR(t);
+
+ // Make adjustment layers behave better
+ KisAdjustmentLayer* adj = dynamic_cast<KisAdjustmentLayer*>(img->activeLayer().data());
+ if (adj) {
+ adj->clearSelection();
+ adj->selection()->invert();
+ } else {
+ dev->selection()->clear();
+ dev->selection()->invert();
+ }
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (img->undo())
+ img->undoAdapter()->addCommand(t);
+}
+
+void KisSelectionManager::deselect()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+ KisSelectedTransaction * t = 0;
+ if (img->undo()) t = new KisSelectedTransaction(i18n("Deselect"), dev);
+ TQ_CHECK_PTR(t);
+
+ // Make adjustment layers behave almost the same (except no reselect)
+ KisAdjustmentLayer* adj = dynamic_cast<KisAdjustmentLayer*>(img->activeLayer().data());
+ if (adj) {
+ adj->clearSelection();
+ } else {
+ dev->deselect();
+ }
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (img->undo())
+ img->undoAdapter()->addCommand(t);
+}
+
+
+void KisSelectionManager::clear()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+
+ KisTransaction * t = 0;
+
+ if (img->undo()) {
+ t = new KisTransaction(i18n("Clear"), dev);
+ }
+
+ dev->clearSelection();
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (img->undo()) img->undoAdapter()->addCommand(t);
+}
+
+void KisSelectionManager::fill(const KisColor& color, bool fillWithPattern, const TQString& transactionText)
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+
+ KisSelectionSP selection = dev->selection();
+
+ KisPaintDeviceSP filled = new KisPaintDevice(dev->colorSpace());
+ KisFillPainter painter(filled);
+
+ if (fillWithPattern) {
+ painter.fillRect(0, 0, img->width(), img->height(),
+ m_parent->currentPattern());
+ } else {
+ painter.fillRect(0, 0, img->width(), img->height(), color);
+ }
+
+ painter.end();
+
+ KisPainter painter2(dev);
+
+ if (img->undo()) painter2.beginTransaction(transactionText);
+ painter2.bltSelection(0, 0, COMPOSITE_OVER, filled, OPACITY_OPAQUE,
+ 0, 0, img->width(), img->height());
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (img->undo()) {
+ img->undoAdapter()->addCommand(painter2.endTransaction());
+ }
+}
+
+void KisSelectionManager::fillForegroundColor()
+{
+ fill(m_parent->fgColor(), false, i18n("Fill with Foreground Color"));
+}
+
+void KisSelectionManager::fillBackgroundColor()
+{
+ fill(m_parent->bgColor(), false, i18n("Fill with Background Color"));
+}
+
+void KisSelectionManager::fillPattern()
+{
+ fill(KisColor(), true, i18n("Fill with Pattern"));
+}
+
+void KisSelectionManager::reselect()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img ->activeDevice();
+ if (!dev) return;
+
+ KisSelectedTransaction * t = 0;
+ if (img->undo()) t = new KisSelectedTransaction(i18n("Reselect"), dev);
+ TQ_CHECK_PTR(t);
+
+ dev->reselect(); // sets hasSelection=true
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (img->undo())
+ img->undoAdapter()->addCommand(t);
+}
+
+
+void KisSelectionManager::invert()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (dev->hasSelection()) {
+ KisSelectionSP s = dev->selection();
+
+ KisSelectedTransaction * t = 0;
+ if (img->undo())
+ {
+ t = new KisSelectedTransaction(i18n("Invert"), dev);
+ TQ_CHECK_PTR(t);
+ }
+
+ s->invert();
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (t) {
+ img->undoAdapter()->addCommand(t);
+ }
+ }
+}
+
+void KisSelectionManager::copySelectionToNewLayer()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ copy();
+ paste();
+}
+
+void KisSelectionManager::cutToNewLayer()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ cut();
+ paste();
+}
+
+
+void KisSelectionManager::feather()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) {
+ // activate it, but don't do anything with it
+ dev->selection();
+ return;
+ }
+
+ KisSelectionSP selection = dev->selection();
+ KisSelectedTransaction * t = 0;
+ if (img->undo()) t = new KisSelectedTransaction(i18n("Feather..."), dev);
+ TQ_CHECK_PTR(t);
+
+
+ // XXX: we should let gaussian blur & others influence alpha channels as well
+ // (on demand of the caller)
+
+ KisConvolutionPainter painter(selection.data());
+
+ KisKernelSP k = new KisKernel();
+ k->width = 3;
+ k->height = 3;
+ k->factor = 16;
+ k->offset = 0;
+ k->data = new TQ_INT32[9];
+ k->data[0] = 1;
+ k->data[1] = 2;
+ k->data[2] = 1;
+ k->data[3] = 2;
+ k->data[4] = 4;
+ k->data[5] = 2;
+ k->data[6] = 1;
+ k->data[7] = 2;
+ k->data[8] = 1;
+
+ TQRect rect = selection->selectedRect();
+ // Make sure we've got enough space around the edges.
+ rect = TQRect(rect.x() - 3, rect.y() - 3, rect.width() + 6, rect.height() + 6);
+ rect &= TQRect(0, 0, img->width(), img->height());
+
+ painter.applyMatrix(k, rect.x(), rect.y(), rect.width(), rect.height(), BORDER_AVOID, KisChannelInfo::FLAG_ALPHA);
+ painter.end();
+
+ dev->setDirty(rect);
+ dev->emitSelectionChanged();
+
+ if (img->undo())
+ img->undoAdapter()->addCommand(t);
+
+}
+
+void KisSelectionManager::toggleDisplaySelection()
+{
+ m_parent->selectionDisplayToggled(displaySelection());
+}
+
+bool KisSelectionManager::displaySelection()
+{
+ return m_toggleDisplaySelection->isChecked();
+}
+// XXX: Maybe move these esoteric functions to plugins?
+void KisSelectionManager::border() {}
+void KisSelectionManager::expand() {}
+void KisSelectionManager::contract() {}
+void KisSelectionManager::similar() {}
+void KisSelectionManager::transform() {}
+void KisSelectionManager::load() {}
+void KisSelectionManager::save() {}
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+void KisSelectionManager::grow (TQ_INT32 xradius, TQ_INT32 yradius)
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+ /*
+ Any bugs in this fuction are probably also in thin_region
+ Blame all bugs in this function on jaycox@gimp.org
+ */
+
+ TQ_UINT8 **buf; // caches the region's pixel data
+ TQ_UINT8 **max; // caches the largest values for each column
+
+ if (xradius <= 0 || yradius <= 0)
+ return;
+
+ KisSelectedTransaction *t = 0;
+
+ if (img->undo()) {
+ t = new KisSelectedTransaction(i18n("Grow"), dev);
+ TQ_CHECK_PTR(t);
+ }
+
+ max = new TQ_UINT8* [layerSize.width() + 2 * xradius];
+ buf = new TQ_UINT8* [yradius + 1];
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ {
+ buf[i] = new TQ_UINT8[layerSize.width()];
+ }
+ TQ_UINT8* buffer = new TQ_UINT8[ ( layerSize.width() + 2 * xradius ) * ( yradius + 1 ) ];
+ for (TQ_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
+ {
+ if (i < xradius)
+ max[i] = buffer;
+ else if (i < layerSize.width() + xradius)
+ max[i] = &buffer[(yradius + 1) * (i - xradius)];
+ else
+ max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
+
+ for (TQ_INT32 j = 0; j < xradius + 1; j++)
+ max[i][j] = 0;
+ }
+ /* offset the max pointer by xradius so the range of the array
+ is [-xradius] to [region->w + xradius] */
+ max += xradius;
+
+ TQ_UINT8* out = new TQ_UINT8[ layerSize.width() ]; // holds the new scan line we are computing
+
+ TQ_INT32* circ = new TQ_INT32[ 2 * xradius + 1 ]; // holds the y coords of the filter's mask
+ computeBorder (circ, xradius, yradius);
+
+ /* offset the circ pointer by xradius so the range of the array
+ is [-xradius] to [xradius] */
+ circ += xradius;
+
+ memset (buf[0], 0, layerSize.width());
+ for (TQ_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
+ {
+ selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
+ }
+
+ for (TQ_INT32 x = 0; x < layerSize.width() ; x++) // set up max for top of image
+ {
+ max[x][0] = 0; // buf[0][x] is always 0
+ max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
+ for (TQ_INT32 j = 2; j < yradius + 1; j++)
+ {
+ max[x][j] = MAX(buf[j][x], max[x][j-1]);
+ }
+ }
+
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++)
+ {
+ rotatePointers (buf, yradius + 1);
+ if (y < layerSize.height() - (yradius))
+ selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
+ else
+ memset (buf[yradius], 0, layerSize.width());
+ for (TQ_INT32 x = 0; x < layerSize.width(); x++) /* update max array */
+ {
+ for (TQ_INT32 i = yradius; i > 0; i--)
+ {
+ max[x][i] = MAX (MAX (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
+ }
+ max[x][0] = buf[0][x];
+ }
+ TQ_INT32 last_max = max[0][circ[-1]];
+ TQ_INT32 last_index = 1;
+ for (TQ_INT32 x = 0; x < layerSize.width(); x++) /* render scan line */
+ {
+ last_index--;
+ if (last_index >= 0)
+ {
+ if (last_max == 255)
+ out[x] = 255;
+ else
+ {
+ last_max = 0;
+ for (TQ_INT32 i = xradius; i >= 0; i--)
+ if (last_max < max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ }
+ else
+ {
+ last_index = xradius;
+ last_max = max[x + xradius][circ[xradius]];
+ for (TQ_INT32 i = xradius - 1; i >= -xradius; i--)
+ if (last_max < max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ }
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
+ }
+ /* undo the offsets to the pointers so we can free the malloced memmory */
+ circ -= xradius;
+ max -= xradius;
+ //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
+ delete[] circ;
+ delete[] buffer;
+ delete[] max;
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ delete[] buf[i];
+ delete[] buf;
+ delete[] out;
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+
+ if (t) {
+ img->undoAdapter()->addCommand(t);
+ }
+}
+
+void KisSelectionManager::shrink (TQ_INT32 xradius, TQ_INT32 yradius, bool edge_lock)
+{
+
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+ /*
+ pretty much the same as fatten_region only different
+ blame all bugs in this function on jaycox@gimp.org
+ */
+ /* If edge_lock is true we assume that pixels outside the region
+ we are passed are identical to the edge pixels.
+ If edge_lock is false, we assume that pixels outside the region are 0
+ */
+ TQ_UINT8 **buf; // caches the the region's pixels
+ TQ_UINT8 **max; // caches the smallest values for each column
+ TQ_INT32 last_max, last_index;
+
+ if (xradius <= 0 || yradius <= 0)
+ return;
+
+ max = new TQ_UINT8* [layerSize.width() + 2 * xradius];
+ buf = new TQ_UINT8* [yradius + 1];
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ {
+ buf[i] = new TQ_UINT8[layerSize.width()];
+ }
+
+ TQ_INT32 buffer_size = (layerSize.width() + 2 * xradius + 1) * (yradius + 1);
+ TQ_UINT8* buffer = new TQ_UINT8[buffer_size];
+
+ if (edge_lock)
+ memset(buffer, 255, buffer_size);
+ else
+ memset(buffer, 0, buffer_size);
+
+ for (TQ_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
+ {
+ if (i < xradius)
+ if (edge_lock)
+ max[i] = buffer;
+ else
+ max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
+ else if (i < layerSize.width() + xradius)
+ max[i] = &buffer[(yradius + 1) * (i - xradius)];
+ else
+ if (edge_lock)
+ max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
+ else
+ max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
+ }
+ if (!edge_lock)
+ for (TQ_INT32 j = 0 ; j < xradius + 1; j++) max[0][j] = 0;
+
+ // offset the max pointer by xradius so the range of the array is [-xradius] to [region->w + xradius]
+ max += xradius;
+
+ TQ_UINT8* out = new TQ_UINT8[layerSize.width()]; // holds the new scan line we are computing
+
+ TQ_INT32* circ = new TQ_INT32[2 * xradius + 1]; // holds the y coords of the filter's mask
+
+ computeBorder (circ, xradius, yradius);
+
+ // offset the circ pointer by xradius so the range of the array is [-xradius] to [xradius]
+ circ += xradius;
+
+ for (TQ_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
+ selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
+
+ if (edge_lock)
+ memcpy (buf[0], buf[1], layerSize.width());
+ else
+ memset (buf[0], 0, layerSize.width());
+
+
+ for (TQ_INT32 x = 0; x < layerSize.width(); x++) // set up max for top of image
+ {
+ max[x][0] = buf[0][x];
+ for (TQ_INT32 j = 1; j < yradius + 1; j++)
+ max[x][j] = MIN(buf[j][x], max[x][j-1]);
+ }
+
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++)
+ {
+ rotatePointers (buf, yradius + 1);
+ if (y < layerSize.height() - yradius)
+ selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
+ else if (edge_lock)
+ memcpy (buf[yradius], buf[yradius - 1], layerSize.width());
+ else
+ memset (buf[yradius], 0, layerSize.width());
+
+ for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // update max array
+ {
+ for (TQ_INT32 i = yradius; i > 0; i--)
+ {
+ max[x][i] = MIN (MIN (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
+ }
+ max[x][0] = buf[0][x];
+ }
+ last_max = max[0][circ[-1]];
+ last_index = 0;
+
+ for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
+ {
+ last_index--;
+ if (last_index >= 0)
+ {
+ if (last_max == 0)
+ out[x] = 0;
+ else
+ {
+ last_max = 255;
+ for (TQ_INT32 i = xradius; i >= 0; i--)
+ if (last_max > max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ }
+ else
+ {
+ last_index = xradius;
+ last_max = max[x + xradius][circ[xradius]];
+ for (TQ_INT32 i = xradius - 1; i >= -xradius; i--)
+ if (last_max > max[x + i][circ[i]])
+ {
+ last_max = max[x + i][circ[i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ }
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
+ }
+
+ // undo the offsets to the pointers so we can free the malloced memmory
+ circ -= xradius;
+ max -= xradius;
+ //free the memmory
+ //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
+ delete[] circ;
+ delete[] buffer;
+ delete[] max;
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ delete buf[i];
+ delete[] buf;
+ delete[] out;
+
+ dev->setDirty(layerSize);
+ dev->emitSelectionChanged();
+}
+
+//Simple convolution filter to smooth a mask (1bpp)
+
+void KisSelectionManager::smooth()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+
+ TQ_UINT8 *buf[3];
+
+ TQ_INT32 width = layerSize.width();
+
+ for (TQ_INT32 i = 0; i < 3; i++) buf[i] = new TQ_UINT8[width + 2];
+
+ TQ_UINT8* out = new TQ_UINT8[width];
+
+ // load top of image
+ selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
+
+ buf[0][0] = buf[0][1];
+ buf[0][width + 1] = buf[0][width];
+
+ memcpy (buf[1], buf[0], width + 2);
+
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++)
+ {
+ if (y + 1 < layerSize.height())
+ {
+ selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
+
+ buf[2][0] = buf[2][1];
+ buf[2][width + 1] = buf[2][width];
+ }
+ else
+ {
+ memcpy (buf[2], buf[1], width + 2);
+ }
+
+ for (TQ_INT32 x = 0 ; x < width; x++)
+ {
+ TQ_INT32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
+ buf[1][x] + buf[2][x+1] + buf[1][x+2] +
+ buf[2][x] + buf[1][x+1] + buf[2][x+2]);
+
+ out[x] = value / 9;
+ }
+
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
+
+ rotatePointers (buf, 3);
+ }
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ delete[] buf[i];
+
+ delete[] out;
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+}
+
+// Erode (radius 1 pixel) a mask (1bpp)
+
+void KisSelectionManager::erode()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+
+ TQ_UINT8* buf[3];
+
+
+ TQ_INT32 width = layerSize.width();
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ buf[i] = new TQ_UINT8[width + 2];
+
+ TQ_UINT8* out = new TQ_UINT8[width];
+
+ // load top of image
+ selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
+
+ buf[0][0] = buf[0][1];
+ buf[0][width + 1] = buf[0][width];
+
+ memcpy (buf[1], buf[0], width + 2);
+
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++)
+ {
+ if (y + 1 < layerSize.height())
+ {
+ selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
+
+ buf[2][0] = buf[2][1];
+ buf[2][width + 1] = buf[2][width];
+ }
+ else
+ {
+ memcpy (buf[2], buf[1], width + 2);
+ }
+
+ for (TQ_INT32 x = 0 ; x < width; x++)
+ {
+ TQ_INT32 min = 255;
+
+ if (buf[0][x+1] < min) min = buf[0][x+1];
+ if (buf[1][x] < min) min = buf[1][x];
+ if (buf[1][x+1] < min) min = buf[1][x+1];
+ if (buf[1][x+2] < min) min = buf[1][x+2];
+ if (buf[2][x+1] < min) min = buf[2][x+1];
+
+ out[x] = min;
+ }
+
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
+
+ rotatePointers (buf, 3);
+ }
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ delete[] buf[i];
+
+ delete[] out;
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+}
+
+// dilate (radius 1 pixel) a mask (1bpp)
+
+void KisSelectionManager::dilate()
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+
+ TQ_UINT8* buf[3];
+
+ TQ_INT32 width = layerSize.width();
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ buf[i] = new TQ_UINT8[width + 2];
+
+ TQ_UINT8* out = new TQ_UINT8[width];
+
+ // load top of image
+ selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
+
+ buf[0][0] = buf[0][1];
+ buf[0][width + 1] = buf[0][width];
+
+ memcpy (buf[1], buf[0], width + 2);
+
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++)
+ {
+ if (y + 1 < layerSize.height())
+ {
+ selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
+
+ buf[2][0] = buf[2][1];
+ buf[2][width + 1] = buf[2][width];
+ }
+ else
+ {
+ memcpy (buf[2], buf[1], width + 2);
+ }
+
+ for (TQ_INT32 x = 0 ; x < width; x++)
+ {
+ TQ_INT32 max = 0;
+
+ if (buf[0][x+1] > max) max = buf[0][x+1];
+ if (buf[1][x] > max) max = buf[1][x];
+ if (buf[1][x+1] > max) max = buf[1][x+1];
+ if (buf[1][x+2] > max) max = buf[1][x+2];
+ if (buf[2][x+1] > max) max = buf[2][x+1];
+
+ out[x] = max;
+ }
+
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
+
+ rotatePointers (buf, 3);
+ }
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ delete[] buf[i];
+
+ delete[] out;
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+}
+
+void KisSelectionManager::border(TQ_INT32 xradius, TQ_INT32 yradius)
+{
+ KisImageSP img = m_parent->currentImg();
+ if (!img) return;
+
+ KisPaintDeviceSP dev = img->activeDevice();
+ if (!dev) return;
+
+ if (!dev->hasSelection()) return;
+ KisSelectionSP selection = dev->selection();
+
+ //determine the layerSize
+ TQRect layerSize = dev->exactBounds();
+
+ /*
+ This function has no bugs, but if you imagine some you can
+ blame them on jaycox@gimp.org
+ */
+ TQ_UINT8 *buf[3];
+ TQ_UINT8 **density;
+ TQ_UINT8 **transition;
+
+ if (xradius == 1 && yradius == 1) // optimize this case specifically
+ {
+ TQ_UINT8* source[3];
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ source[i] = new TQ_UINT8[layerSize.width()];
+
+ TQ_UINT8* transition = new TQ_UINT8[layerSize.width()];
+
+ selection->readBytes(source[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
+ memcpy (source[1], source[0], layerSize.width());
+ if (layerSize.height() > 1)
+ selection->readBytes(source[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
+ else
+ memcpy (source[2], source[1], layerSize.width());
+
+ computeTransition (transition, source, layerSize.width());
+ selection->writeBytes(transition, layerSize.x(), layerSize.y(), layerSize.width(), 1);
+
+ for (TQ_INT32 y = 1; y < layerSize.height(); y++)
+ {
+ rotatePointers (source, 3);
+ if (y + 1 < layerSize.height())
+ selection->readBytes(source[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
+ else
+ memcpy(source[2], source[1], layerSize.width());
+ computeTransition (transition, source, layerSize.width());
+ selection->writeBytes(transition, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
+ }
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ delete[] source[i];
+ delete[] transition;
+ return;
+ }
+
+ TQ_INT32* max = new TQ_INT32[layerSize.width() + 2 * xradius];
+ for (TQ_INT32 i = 0; i < (layerSize.width() + 2 * xradius); i++)
+ max[i] = yradius + 2;
+ max += xradius;
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ buf[i] = new TQ_UINT8[layerSize.width()];
+
+ transition = new TQ_UINT8*[yradius + 1];
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ {
+ transition[i] = new TQ_UINT8[layerSize.width() + 2 * xradius];
+ memset(transition[i], 0, layerSize.width() + 2 * xradius);
+ transition[i] += xradius;
+ }
+ TQ_UINT8* out = new TQ_UINT8[layerSize.width()];
+ density = new TQ_UINT8*[2 * xradius + 1];
+ density += xradius;
+
+ for (TQ_INT32 x = 0; x < (xradius + 1); x++) // allocate density[][]
+ {
+ density[ x] = new TQ_UINT8[2 * yradius + 1];
+ density[ x] += yradius;
+ density[-x] = density[x];
+ }
+ for (TQ_INT32 x = 0; x < (xradius + 1); x++) // compute density[][]
+ {
+ double tmpx, tmpy, dist;
+ TQ_UINT8 a;
+
+ if (x > 0)
+ tmpx = x - 0.5;
+ else if (x < 0)
+ tmpx = x + 0.5;
+ else
+ tmpx = 0.0;
+
+ for (TQ_INT32 y = 0; y < (yradius + 1); y++)
+ {
+ if (y > 0)
+ tmpy = y - 0.5;
+ else if (y < 0)
+ tmpy = y + 0.5;
+ else
+ tmpy = 0.0;
+ dist = ((tmpy * tmpy) / (yradius * yradius) +
+ (tmpx * tmpx) / (xradius * xradius));
+ if (dist < 1.0)
+ a = 255 * (TQ_UINT8)(1.0 - sqrt (dist));
+ else
+ a = 0;
+ density[ x][ y] = a;
+ density[ x][-y] = a;
+ density[-x][ y] = a;
+ density[-x][-y] = a;
+ }
+ }
+ selection->readBytes(buf[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
+ memcpy (buf[1], buf[0], layerSize.width());
+ if (layerSize.height() > 1)
+ selection->readBytes(buf[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
+ else
+ memcpy (buf[2], buf[1], layerSize.width());
+ computeTransition (transition[1], buf, layerSize.width());
+
+ for (TQ_INT32 y = 1; y < yradius && y + 1 < layerSize.height(); y++) // set up top of image
+ {
+ rotatePointers (buf, 3);
+ selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
+ computeTransition (transition[y + 1], buf, layerSize.width());
+ }
+ for (TQ_INT32 x = 0; x < layerSize.width(); x++) // set up max[] for top of image
+ {
+ max[x] = -(yradius + 7);
+ for (TQ_INT32 j = 1; j < yradius + 1; j++)
+ if (transition[j][x])
+ {
+ max[x] = j;
+ break;
+ }
+ }
+ for (TQ_INT32 y = 0; y < layerSize.height(); y++) // main calculation loop
+ {
+ rotatePointers (buf, 3);
+ rotatePointers (transition, yradius + 1);
+ if (y < layerSize.height() - (yradius + 1))
+ {
+ selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + yradius + 1, layerSize.width(), 1);
+ computeTransition (transition[yradius], buf, layerSize.width());
+ }
+ else
+ memcpy (transition[yradius], transition[yradius - 1], layerSize.width());
+
+ for (TQ_INT32 x = 0; x < layerSize.width(); x++) // update max array
+ {
+ if (max[x] < 1)
+ {
+ if (max[x] <= -yradius)
+ {
+ if (transition[yradius][x])
+ max[x] = yradius;
+ else
+ max[x]--;
+ }
+ else
+ if (transition[-max[x]][x])
+ max[x] = -max[x];
+ else if (transition[-max[x] + 1][x])
+ max[x] = -max[x] + 1;
+ else
+ max[x]--;
+ }
+ else
+ max[x]--;
+ if (max[x] < -yradius - 1)
+ max[x] = -yradius - 1;
+ }
+ TQ_UINT8 last_max = max[0][density[-1]];
+ TQ_INT32 last_index = 1;
+ for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
+ {
+ last_index--;
+ if (last_index >= 0)
+ {
+ last_max = 0;
+ for (TQ_INT32 i = xradius; i >= 0; i--)
+ if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x+i]] > last_max)
+ {
+ last_max = density[i][max[x + i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ else
+ {
+ last_max = 0;
+ for (TQ_INT32 i = xradius; i >= -xradius; i--)
+ if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x + i]] > last_max)
+ {
+ last_max = density[i][max[x + i]];
+ last_index = i;
+ }
+ out[x] = last_max;
+ }
+ if (last_max == 0)
+ {
+ TQ_INT32 i;
+ for (i = x + 1; i < layerSize.width(); i++)
+ {
+ if (max[i] >= -yradius)
+ break;
+ }
+ if (i - x > xradius)
+ {
+ for (; x < i - xradius; x++)
+ out[x] = 0;
+ x--;
+ }
+ last_index = xradius;
+ }
+ }
+ selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
+ }
+ delete [] out;
+
+ for (TQ_INT32 i = 0; i < 3; i++)
+ delete buf[i];
+
+ max -= xradius;
+ delete[] max;
+
+ for (TQ_INT32 i = 0; i < yradius + 1; i++)
+ {
+ transition[i] -= xradius;
+ delete transition[i];
+ }
+ delete[] transition;
+
+ for (TQ_INT32 i = 0; i < xradius + 1 ; i++)
+ {
+ density[i] -= yradius;
+ delete density[i];
+ }
+ density -= xradius;
+ delete[] density;
+
+ dev->setDirty();
+ dev->emitSelectionChanged();
+}
+
+#define RINT(x) floor ((x) + 0.5)
+
+void KisSelectionManager::computeBorder (TQ_INT32 *circ, TQ_INT32 xradius, TQ_INT32 yradius)
+{
+ Q_ASSERT(xradius != 0);
+ TQ_INT32 i;
+ TQ_INT32 diameter = xradius * 2 + 1;
+ double tmp;
+
+ for (i = 0; i < diameter; i++)
+ {
+ if (i > xradius)
+ tmp = (i - xradius) - 0.5;
+ else if (i < xradius)
+ tmp = (xradius - i) - 0.5;
+ else
+ tmp = 0.0;
+
+ circ[i] = (TQ_INT32) RINT (yradius / (double) xradius * sqrt (xradius * xradius - tmp * tmp));
+ }
+}
+
+void KisSelectionManager::rotatePointers (TQ_UINT8 **p, TQ_UINT32 n)
+{
+ TQ_UINT32 i;
+ TQ_UINT8 *tmp;
+
+ tmp = p[0];
+
+ for (i = 0; i < n - 1; i++) p[i] = p[i + 1];
+
+ p[i] = tmp;
+}
+
+void KisSelectionManager::computeTransition (TQ_UINT8* transition, TQ_UINT8** buf, TQ_INT32 width)
+{
+ TQ_INT32 x = 0;
+
+ if (width == 1)
+ {
+ if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
+ transition[x] = 255;
+ else
+ transition[x] = 0;
+ return;
+ }
+ if (buf[1][x] > 127)
+ {
+ if ( buf[0][x] < 128 || buf[0][x + 1] < 128 ||
+ buf[1][x + 1] < 128 ||
+ buf[2][x] < 128 || buf[2][x + 1] < 128 )
+ transition[x] = 255;
+ else
+ transition[x] = 0;
+ }
+ else
+ transition[x] = 0;
+ for (TQ_INT32 x = 1; x < width - 1; x++)
+ {
+ if (buf[1][x] >= 128)
+ {
+ if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
+ buf[1][x - 1] < 128 || buf[1][x + 1] < 128 ||
+ buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
+ transition[x] = 255;
+ else
+ transition[x] = 0;
+ }
+ else
+ transition[x] = 0;
+ }
+ if (buf[1][x] >= 128)
+ {
+ if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
+ buf[1][x - 1] < 128 ||
+ buf[2][x - 1] < 128 || buf[2][x] < 128)
+ transition[x] = 255;
+ else
+ transition[x] = 0;
+ }
+ else
+ transition[x] = 0;
+}
+
+#include "kis_selection_manager.moc"