chalk

kis_selection_manager.cpp
1/*
2 * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include <tqobject.h>
20#include <tqapplication.h>
21#include <tqclipboard.h>
22#include <tqcolor.h>
23#include <tqcursor.h>
24
25#include <kdebug.h>
26#include <tdeaction.h>
27#include <tdelocale.h>
28#include <kstdaction.h>
29
30#include <KoDocument.h>
31#include <KoMainWindow.h>
32#include <KoQueryTrader.h>
33
34#include "kis_cursor.h"
35#include "kis_part_layer.h"
36#include "kis_adjustment_layer.h"
37#include "kis_clipboard.h"
38#include "kis_types.h"
39#include "kis_view.h"
40#include "kis_doc.h"
41#include "kis_image.h"
42#include "kis_selection.h"
43#include "kis_selection_manager.h"
44#include "kis_painter.h"
45#include "kis_iterators_pixel.h"
46#include "kis_iteratorpixeltrait.h"
47#include "kis_layer.h"
48#include "kis_group_layer.h"
49#include "kis_paint_layer.h"
50#include "kis_paint_device.h"
51#include "kis_channelinfo.h"
52#include "kis_dlg_apply_profile.h"
53#include "kis_config.h"
54#include "kis_debug_areas.h"
55#include "kis_transaction.h"
56#include "kis_undo_adapter.h"
57#include "kis_selected_transaction.h"
58#include "kis_convolution_painter.h"
59#include "kis_integer_maths.h"
60#include "kis_fill_painter.h"
61#include "kis_canvas.h"
62
63KisSelectionManager::KisSelectionManager(KisView * parent, KisDoc * doc)
64 : m_parent(parent),
65 m_doc(doc),
66 m_copy(0),
67 m_cut(0),
68 m_paste(0),
69 m_pasteNew(0),
70 m_cutToNewLayer(0),
71 m_selectAll(0),
72 m_deselect(0),
73 m_clear(0),
74 m_reselect(0),
75 m_invert(0),
76 m_toNewLayer(0),
77 m_feather(0),
78 m_border(0),
79 m_expand(0),
80 m_smooth(0),
81 m_contract(0),
82 m_similar(0),
83 m_transform(0),
84 m_load(0),
85 m_save(0),
86 m_fillForegroundColor(0),
87 m_fillBackgroundColor(0),
88 m_fillPattern(0)
89{
90 m_pluginActions.setAutoDelete(true);
91 m_clipboard = KisClipboard::instance();
92}
93
94KisSelectionManager::~KisSelectionManager()
95{
96 m_pluginActions.clear();
97}
98
99
100void KisSelectionManager::setup(TDEActionCollection * collection)
101{
102 // XXX: setup shortcuts!
103
104 m_cut = KStdAction::cut(this,
105 TQ_SLOT(cut()),
106 collection,
107 "cut");
108
109 m_copy = KStdAction::copy(this,
110 TQ_SLOT(copy()),
111 collection,
112 "copy");
113
114 m_paste = KStdAction::paste(this,
115 TQ_SLOT(paste()),
116 collection,
117 "paste");
118
119 m_pasteNew = new TDEAction(i18n("Paste into &New Image"),
120 0, 0,
121 this, TQ_SLOT(pasteNew()),
122 collection,
123 "paste_new");
124
125
126 m_selectAll = KStdAction::selectAll(this,
127 TQ_SLOT(selectAll()),
128 collection,
129 "select_all");
130
131 m_deselect = KStdAction::deselect(this,
132 TQ_SLOT(deselect()),
133 collection,
134 "deselect");
135
136
137 m_clear = KStdAction::clear(this,
138 TQ_SLOT(clear()),
139 collection,
140 "clear");
141
142 m_reselect = new TDEAction(i18n("&Reselect"),
143 0, "Ctrl+Shift+D",
144 this, TQ_SLOT(reselect()),
145 collection, "reselect");
146
147 m_invert = new TDEAction(i18n("&Invert"),
148 0, "Ctrl+I",
149 this, TQ_SLOT(invert()),
150 collection, "invert");
151
152
153 m_toNewLayer = new TDEAction(i18n("Copy Selection to New Layer"),
154 0, "Ctrl+J",
155 this, TQ_SLOT(copySelectionToNewLayer()),
156 collection, "copy_selection_to_new_layer");
157
158
159 m_cutToNewLayer = new TDEAction(i18n("Cut Selection to New Layer"),
160 0, "Ctrl+Shift+J",
161 this, TQ_SLOT(cutToNewLayer()),
162 collection, "cut_selection_to_new_layer");
163
164 m_feather = new TDEAction(i18n("Feather"),
165 0, "Ctrl+Alt+D",
166 this, TQ_SLOT(feather()),
167 collection, "feather");
168
169 m_fillForegroundColor = new TDEAction(i18n("Fill with Foreground Color"),
170 "Alt+backspace", this,
171 TQ_SLOT(fillForegroundColor()),
172 collection,
173 "fill_selection_foreground_color");
174 m_fillBackgroundColor = new TDEAction(i18n("Fill with Background Color"),
175 "backspace", this,
176 TQ_SLOT(fillBackgroundColor()),
177 collection,
178 "fill_selection_background_color");
179 m_fillPattern = new TDEAction(i18n("Fill with Pattern"),
180 0, this,
181 TQ_SLOT(fillPattern()),
182 collection,
183 "fill_selection_pattern");
184
185 m_toggleDisplaySelection = new TDEToggleAction(i18n("Display Selection"), "Ctrl+h", this, TQ_SLOT(toggleDisplaySelection()), collection, "toggle_display_selection");
186 m_toggleDisplaySelection->setCheckedState(KGuiItem(i18n("Hide Selection")));
187 m_toggleDisplaySelection->setChecked(true);
188
189 m_border =
190 new TDEAction(i18n("Border..."),
191 0, 0,
192 this, TQ_SLOT(border()),
193 collection, "border");
194 m_expand =
195 new TDEAction(i18n("Expand..."),
196 0, 0,
197 this, TQ_SLOT(expand()),
198 collection, "expand");
199
200 m_smooth =
201 new TDEAction(i18n("Smooth..."),
202 0, 0,
203 this, TQ_SLOT(smooth()),
204 collection, "smooth");
205
206
207 m_contract =
208 new TDEAction(i18n("Contract..."),
209 0, 0,
210 this, TQ_SLOT(contract()),
211 collection, "contract");
212 m_similar =
213 new TDEAction(i18n("Similar"),
214 0, 0,
215 this, TQ_SLOT(similar()),
216 collection, "similar");
217
218
219 m_transform
220 = new TDEAction(i18n("Transform..."),
221 0, 0,
222 this, TQ_SLOT(transform()),
223 collection, "transform_selection");
224
225
226// m_load
227// = new TDEAction(i18n("Load..."),
228// 0, 0,
229// this, TQ_SLOT(load()),
230// collection, "load_selection");
231//
232//
233// m_save
234// = new TDEAction(i18n("Save As..."),
235// 0, 0,
236// this, TQ_SLOT(save()),
237// collection, "save_selection");
238
239 TQClipboard *cb = TQApplication::clipboard();
240 connect(cb, TQ_SIGNAL(dataChanged()), TQ_SLOT(clipboardDataChanged()));
241}
242
243void KisSelectionManager::clipboardDataChanged()
244{
245 updateGUI();
246}
247
248
249void KisSelectionManager::addSelectionAction(TDEAction * action)
250{
251 m_pluginActions.append(action);
252}
253
254
255void KisSelectionManager::updateGUI()
256{
257 Q_ASSERT(m_parent);
258 Q_ASSERT(m_clipboard);
259
260 if (m_parent == 0) {
261 // "Eek, no parent!
262 return;
263 }
264
265 if (m_clipboard == 0) {
266 // Eek, no clipboard!
267 return;
268 }
269
270 KisImageSP img = m_parent->currentImg();
271 KisLayerSP l = 0;
272 KisPaintDeviceSP dev = 0;
273
274 bool enable = false;
275 if (img && img->activeDevice() && img->activeLayer()) {
276 l = img->activeLayer();
277 dev = img->activeDevice();
278
279
280 KisPartLayer * partLayer = dynamic_cast<KisPartLayer*>(l.data());
281 KisAdjustmentLayer * adjLayer = dynamic_cast<KisAdjustmentLayer*>(l.data());
282
283 enable = l && dev&& dev->hasSelection() && !l->locked() && l->visible() && (partLayer==0);
284
285 if(dev && !adjLayer)
286 m_reselect->setEnabled( dev->selectionDeselected() );
287 if (adjLayer) // There's no reselect for adjustment layers
288 m_reselect->setEnabled(false);
289 }
290
291 m_cut->setEnabled(enable);
292 m_cutToNewLayer->setEnabled(enable);
293 m_selectAll->setEnabled(img != 0);
294 m_deselect->setEnabled(enable);
295 m_clear->setEnabled(enable);
296 m_fillForegroundColor->setEnabled(enable);
297 m_fillBackgroundColor->setEnabled(enable);
298 m_fillPattern->setEnabled(enable);
299 m_invert->setEnabled(enable);
300
301 m_feather->setEnabled(enable);
302
303 m_border->setEnabled(enable);
304 m_expand->setEnabled(enable);
305 m_smooth->setEnabled(enable);
306 m_contract->setEnabled(enable);
307 m_similar->setEnabled(enable);
308 m_transform->setEnabled(enable);
309// m_load->setEnabled(enable);
310// m_save->setEnabled(enable);
311
312
313 TDEAction * a;
314 for (a = m_pluginActions.first(); a; a = m_pluginActions.next()) {
315 a->setEnabled(img != 0);
316 }
317
318 // You can copy from locked layers and paste the clip into a new layer, even when
319 // the current layer is locked.
320 enable = false;
321 if (img && l && dev) {
322 enable = dev->hasSelection() && l->visible();
323 }
324
325 m_copy->setEnabled(enable);
326 m_paste->setEnabled(img != 0 && m_clipboard->hasClip());
327 m_pasteNew->setEnabled(img != 0 && m_clipboard->hasClip());
328 m_toNewLayer->setEnabled(enable);
329
330 m_parent->updateStatusBarSelectionLabel();
331
332}
333
334void KisSelectionManager::imgSelectionChanged(KisImageSP img)
335{
336 if (img == m_parent->currentImg()) {
337 updateGUI();
338 }
339}
340
341void KisSelectionManager::cut()
342{
343 KisImageSP img = m_parent->currentImg();
344 if (!img) return;
345
346 KisPaintDeviceSP dev = img->activeDevice();
347 if (!dev) return;
348
349 if (!dev->hasSelection()) return;
350
351 copy();
352
353 KisSelectedTransaction *t = 0;
354
355 if (img->undo()) {
356 t = new KisSelectedTransaction(i18n("Cut"), dev);
357 TQ_CHECK_PTR(t);
358 }
359
360 dev->clearSelection();
361 dev->deselect();
362 dev->emitSelectionChanged();
363
364 if (img->undo()) {
365 img->undoAdapter()->addCommand(t);
366 }
367}
368
369void KisSelectionManager::copy()
370{
371 KisImageSP img = m_parent->currentImg();
372 if (!img) return;
373
374 KisPaintDeviceSP dev = img->activeDevice();
375 if (!dev) return;
376
377 if (!dev->hasSelection()) return;
378
379 KisSelectionSP selection = dev->selection();
380
381 TQRect r = selection->selectedExactRect();
382
383 KisPaintDeviceSP clip = new KisPaintDevice(dev->colorSpace(), "clip");
384 TQ_CHECK_PTR(clip);
385
386 KisColorSpace * cs = clip->colorSpace();
387
388 // TODO if the source is linked... copy from all linked layers?!?
389
390 // Copy image data
391 KisPainter gc;
392 gc.begin(clip);
393 gc.bitBlt(0, 0, COMPOSITE_COPY, dev, r.x(), r.y(), r.width(), r.height());
394 gc.end();
395
396 // Apply selection mask.
397
398 for (TQ_INT32 y = 0; y < r.height(); y++) {
399 KisHLineIteratorPixel layerIt = clip->createHLineIterator(0, y, r.width(), true);
400 KisHLineIteratorPixel selectionIt = selection->createHLineIterator(r.x(), r.y() + y, r.width(), false);
401
402 while (!layerIt.isDone()) {
403
404 cs->applyAlphaU8Mask( layerIt.rawData(), selectionIt.rawData(), 1 );
405
406
407 ++layerIt;
408 ++selectionIt;
409 }
410 }
411
412 m_clipboard->setClip(clip);
413 imgSelectionChanged(m_parent->currentImg());
414}
415
416
417KisLayerSP KisSelectionManager::paste()
418{
419 KisImageSP img = m_parent->currentImg();
420 if (!img) return 0;
421
422 KisPaintDeviceSP clip = m_clipboard->clip();
423
424 if (clip) {
425 TQApplication::setOverrideCursor(KisCursor::waitCursor());
426 KisPaintLayer *layer = new KisPaintLayer(img, img->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE);
427 TQ_CHECK_PTR(layer);
428
429 TQRect r = clip->exactBounds();
430 KisPainter gc;
431 gc.begin(layer->paintDevice());
432 gc.bitBlt(0, 0, COMPOSITE_COPY, clip, r.x(), r.y(), r.width(), r.height());
433 gc.end();
434
435 //figure out where to position the clip
436 KisCanvasController *cc = m_parent->getCanvasController();
437 TQPoint center = cc->viewToWindow(TQPoint(cc->kiscanvas()->width()/2, cc->kiscanvas()->height()/2));
438 TQPoint bottomright = cc->viewToWindow(TQPoint(cc->kiscanvas()->width(), cc->kiscanvas()->height()));
439 if(bottomright.x() > img->width())
440 center.setX(img->width()/2);
441 if(bottomright.y() > img->height())
442 center.setY(img->height()/2);
443 center -= TQPoint(r.width()/2, r.height()/2);
444 layer->setX(center.x());
445 layer->setY(center.y());
446
447/*XXX CBR have an idea of asking the user if he is about to paste a clip ion another cs than that of
448 the image if that is what he want rather than silently converting
449 if (clip->colorSpace != img ->colorSpace())
450 if (dlg->exec() == TQDialog::Accepted)
451 layer->convertTo(img->colorSpace());
452*/
453 TQApplication::restoreOverrideCursor();
454 if(img->addLayer(layer, img->activeLayer()->parent(), img->activeLayer()))
455 {
456 return layer;
457 } else {
458 return 0;
459 }
460 }
461 return 0;
462}
463
464void KisSelectionManager::pasteNew()
465{
466 KisPaintDeviceSP clip = m_clipboard->clip();
467 if (!clip) return;
468
469 TQRect r = clip->exactBounds();
470 if (r.width() < 1 && r.height() < 1) {
471 // Don't paste empty clips
472 return;
473 }
474
475 const TQCString mimetype = KoDocument::readNativeFormatMimeType();
476 KoDocumentEntry entry = KoDocumentEntry::queryByMimeType( mimetype );
477 KisDoc * doc = (KisDoc*) entry.createDoc();
478
479 Q_ASSERT(doc->undoAdapter() != 0);
480 doc->undoAdapter()->setUndo(false);
481
482 KisImageSP img = new KisImage(doc->undoAdapter(), r.width(), r.height(), clip->colorSpace(), "Pasted");
483 KisPaintLayer *layer = new KisPaintLayer(img, clip->name(), OPACITY_OPAQUE, clip->colorSpace());
484
485 KisPainter p(layer->paintDevice());
486 p.bitBlt(0, 0, COMPOSITE_COPY, clip, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height());
487 p.end();
488
489 img->addLayer(layer, img->rootLayer(), 0);
490 doc->setCurrentImage(img);
491
492 doc->undoAdapter()->setUndo(true);
493
494 KoMainWindow *win = new KoMainWindow( doc->instance() );
495 win->show();
496 win->setRootDocument( doc );
497}
498
499void KisSelectionManager::selectAll()
500{
501 KisImageSP img = m_parent->currentImg();
502 if (!img) return;
503
504 KisPaintDeviceSP dev = img->activeDevice();
505 if (!dev) return;
506
507 KisSelectedTransaction * t = 0;
508 if (img->undo()) t = new KisSelectedTransaction(i18n("Select All"), dev);
509 TQ_CHECK_PTR(t);
510
511 // Make adjustment layers behave better
512 KisAdjustmentLayer* adj = dynamic_cast<KisAdjustmentLayer*>(img->activeLayer().data());
513 if (adj) {
514 adj->clearSelection();
515 adj->selection()->invert();
516 } else {
517 dev->selection()->clear();
518 dev->selection()->invert();
519 }
520 dev->setDirty();
521 dev->emitSelectionChanged();
522
523 if (img->undo())
524 img->undoAdapter()->addCommand(t);
525}
526
527void KisSelectionManager::deselect()
528{
529 KisImageSP img = m_parent->currentImg();
530 if (!img) return;
531
532 KisPaintDeviceSP dev = img->activeDevice();
533 if (!dev) return;
534 KisSelectedTransaction * t = 0;
535 if (img->undo()) t = new KisSelectedTransaction(i18n("Deselect"), dev);
536 TQ_CHECK_PTR(t);
537
538 // Make adjustment layers behave almost the same (except no reselect)
539 KisAdjustmentLayer* adj = dynamic_cast<KisAdjustmentLayer*>(img->activeLayer().data());
540 if (adj) {
541 adj->clearSelection();
542 } else {
543 dev->deselect();
544 }
545 dev->setDirty();
546 dev->emitSelectionChanged();
547
548 if (img->undo())
549 img->undoAdapter()->addCommand(t);
550}
551
552
553void KisSelectionManager::clear()
554{
555 KisImageSP img = m_parent->currentImg();
556 if (!img) return;
557
558 KisPaintDeviceSP dev = img->activeDevice();
559 if (!dev) return;
560
561 if (!dev->hasSelection()) return;
562
563 KisTransaction * t = 0;
564
565 if (img->undo()) {
566 t = new KisTransaction(i18n("Clear"), dev);
567 }
568
569 dev->clearSelection();
570 dev->setDirty();
571 dev->emitSelectionChanged();
572
573 if (img->undo()) img->undoAdapter()->addCommand(t);
574}
575
576void KisSelectionManager::fill(const KisColor& color, bool fillWithPattern, const TQString& transactionText)
577{
578 KisImageSP img = m_parent->currentImg();
579 if (!img) return;
580
581 KisPaintDeviceSP dev = img->activeDevice();
582 if (!dev) return;
583
584 if (!dev->hasSelection()) return;
585
586 KisSelectionSP selection = dev->selection();
587
588 KisPaintDeviceSP filled = new KisPaintDevice(dev->colorSpace());
589 KisFillPainter painter(filled);
590
591 if (fillWithPattern) {
592 painter.fillRect(0, 0, img->width(), img->height(),
593 m_parent->currentPattern());
594 } else {
595 painter.fillRect(0, 0, img->width(), img->height(), color);
596 }
597
598 painter.end();
599
600 KisPainter painter2(dev);
601
602 if (img->undo()) painter2.beginTransaction(transactionText);
603 painter2.bltSelection(0, 0, COMPOSITE_OVER, filled, OPACITY_OPAQUE,
604 0, 0, img->width(), img->height());
605
606 dev->setDirty();
607 dev->emitSelectionChanged();
608
609 if (img->undo()) {
610 img->undoAdapter()->addCommand(painter2.endTransaction());
611 }
612}
613
614void KisSelectionManager::fillForegroundColor()
615{
616 fill(m_parent->fgColor(), false, i18n("Fill with Foreground Color"));
617}
618
619void KisSelectionManager::fillBackgroundColor()
620{
621 fill(m_parent->bgColor(), false, i18n("Fill with Background Color"));
622}
623
624void KisSelectionManager::fillPattern()
625{
626 fill(KisColor(), true, i18n("Fill with Pattern"));
627}
628
629void KisSelectionManager::reselect()
630{
631 KisImageSP img = m_parent->currentImg();
632 if (!img) return;
633
634 KisPaintDeviceSP dev = img ->activeDevice();
635 if (!dev) return;
636
637 KisSelectedTransaction * t = 0;
638 if (img->undo()) t = new KisSelectedTransaction(i18n("Reselect"), dev);
639 TQ_CHECK_PTR(t);
640
641 dev->reselect(); // sets hasSelection=true
642 dev->setDirty();
643 dev->emitSelectionChanged();
644
645 if (img->undo())
646 img->undoAdapter()->addCommand(t);
647}
648
649
650void KisSelectionManager::invert()
651{
652 KisImageSP img = m_parent->currentImg();
653 if (!img) return;
654
655 KisPaintDeviceSP dev = img->activeDevice();
656 if (!dev) return;
657
658 if (dev->hasSelection()) {
659 KisSelectionSP s = dev->selection();
660
661 KisSelectedTransaction * t = 0;
662 if (img->undo())
663 {
664 t = new KisSelectedTransaction(i18n("Invert"), dev);
665 TQ_CHECK_PTR(t);
666 }
667
668 s->invert();
669 dev->setDirty();
670 dev->emitSelectionChanged();
671
672 if (t) {
673 img->undoAdapter()->addCommand(t);
674 }
675 }
676}
677
678void KisSelectionManager::copySelectionToNewLayer()
679{
680 KisImageSP img = m_parent->currentImg();
681 if (!img) return;
682
683 KisPaintDeviceSP dev = img->activeDevice();
684 if (!dev) return;
685
686 copy();
687 paste();
688}
689
690void KisSelectionManager::cutToNewLayer()
691{
692 KisImageSP img = m_parent->currentImg();
693 if (!img) return;
694
695 KisPaintDeviceSP dev = img->activeDevice();
696 if (!dev) return;
697
698 cut();
699 paste();
700}
701
702
703void KisSelectionManager::feather()
704{
705 KisImageSP img = m_parent->currentImg();
706 if (!img) return;
707 KisPaintDeviceSP dev = img->activeDevice();
708 if (!dev) return;
709
710 if (!dev->hasSelection()) {
711 // activate it, but don't do anything with it
712 dev->selection();
713 return;
714 }
715
716 KisSelectionSP selection = dev->selection();
717 KisSelectedTransaction * t = 0;
718 if (img->undo()) t = new KisSelectedTransaction(i18n("Feather..."), dev);
719 TQ_CHECK_PTR(t);
720
721
722 // XXX: we should let gaussian blur & others influence alpha channels as well
723 // (on demand of the caller)
724
725 KisConvolutionPainter painter(selection.data());
726
727 KisKernelSP k = new KisKernel();
728 k->width = 3;
729 k->height = 3;
730 k->factor = 16;
731 k->offset = 0;
732 k->data = new TQ_INT32[9];
733 k->data[0] = 1;
734 k->data[1] = 2;
735 k->data[2] = 1;
736 k->data[3] = 2;
737 k->data[4] = 4;
738 k->data[5] = 2;
739 k->data[6] = 1;
740 k->data[7] = 2;
741 k->data[8] = 1;
742
743 TQRect rect = selection->selectedRect();
744 // Make sure we've got enough space around the edges.
745 rect = TQRect(rect.x() - 3, rect.y() - 3, rect.width() + 6, rect.height() + 6);
746 rect &= TQRect(0, 0, img->width(), img->height());
747
748 painter.applyMatrix(k, rect.x(), rect.y(), rect.width(), rect.height(), BORDER_AVOID, KisChannelInfo::FLAG_ALPHA);
749 painter.end();
750
751 dev->setDirty(rect);
752 dev->emitSelectionChanged();
753
754 if (img->undo())
755 img->undoAdapter()->addCommand(t);
756
757}
758
759void KisSelectionManager::toggleDisplaySelection()
760{
761 m_parent->selectionDisplayToggled(displaySelection());
762}
763
765{
766 return m_toggleDisplaySelection->isChecked();
767}
768// XXX: Maybe move these esoteric functions to plugins?
769void KisSelectionManager::border() {}
770void KisSelectionManager::expand() {}
771void KisSelectionManager::contract() {}
772void KisSelectionManager::similar() {}
773void KisSelectionManager::transform() {}
774void KisSelectionManager::load() {}
775void KisSelectionManager::save() {}
776
777#define MAX(a, b) ((a) > (b) ? (a) : (b))
778#define MIN(a, b) ((a) < (b) ? (a) : (b))
779
780void KisSelectionManager::grow (TQ_INT32 xradius, TQ_INT32 yradius)
781{
782 KisImageSP img = m_parent->currentImg();
783 if (!img) return;
784
785 KisPaintDeviceSP dev = img->activeDevice();
786 if (!dev) return;
787
788 if (!dev->hasSelection()) return;
789 KisSelectionSP selection = dev->selection();
790
791 //determine the layerSize
792 TQRect layerSize = dev->exactBounds();
793 /*
794 Any bugs in this fuction are probably also in thin_region
795 Blame all bugs in this function on jaycox@gimp.org
796 */
797
798 TQ_UINT8 **buf; // caches the region's pixel data
799 TQ_UINT8 **max; // caches the largest values for each column
800
801 if (xradius <= 0 || yradius <= 0)
802 return;
803
804 KisSelectedTransaction *t = 0;
805
806 if (img->undo()) {
807 t = new KisSelectedTransaction(i18n("Grow"), dev);
808 TQ_CHECK_PTR(t);
809 }
810
811 max = new TQ_UINT8* [layerSize.width() + 2 * xradius];
812 buf = new TQ_UINT8* [yradius + 1];
813 for (TQ_INT32 i = 0; i < yradius + 1; i++)
814 {
815 buf[i] = new TQ_UINT8[layerSize.width()];
816 }
817 TQ_UINT8* buffer = new TQ_UINT8[ ( layerSize.width() + 2 * xradius ) * ( yradius + 1 ) ];
818 for (TQ_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
819 {
820 if (i < xradius)
821 max[i] = buffer;
822 else if (i < layerSize.width() + xradius)
823 max[i] = &buffer[(yradius + 1) * (i - xradius)];
824 else
825 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
826
827 for (TQ_INT32 j = 0; j < xradius + 1; j++)
828 max[i][j] = 0;
829 }
830 /* offset the max pointer by xradius so the range of the array
831 is [-xradius] to [region->w + xradius] */
832 max += xradius;
833
834 TQ_UINT8* out = new TQ_UINT8[ layerSize.width() ]; // holds the new scan line we are computing
835
836 TQ_INT32* circ = new TQ_INT32[ 2 * xradius + 1 ]; // holds the y coords of the filter's mask
837 computeBorder (circ, xradius, yradius);
838
839 /* offset the circ pointer by xradius so the range of the array
840 is [-xradius] to [xradius] */
841 circ += xradius;
842
843 memset (buf[0], 0, layerSize.width());
844 for (TQ_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
845 {
846 selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
847 }
848
849 for (TQ_INT32 x = 0; x < layerSize.width() ; x++) // set up max for top of image
850 {
851 max[x][0] = 0; // buf[0][x] is always 0
852 max[x][1] = buf[1][x]; // MAX (buf[1][x], max[x][0]) always = buf[1][x]
853 for (TQ_INT32 j = 2; j < yradius + 1; j++)
854 {
855 max[x][j] = MAX(buf[j][x], max[x][j-1]);
856 }
857 }
858
859 for (TQ_INT32 y = 0; y < layerSize.height(); y++)
860 {
861 rotatePointers (buf, yradius + 1);
862 if (y < layerSize.height() - (yradius))
863 selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
864 else
865 memset (buf[yradius], 0, layerSize.width());
866 for (TQ_INT32 x = 0; x < layerSize.width(); x++) /* update max array */
867 {
868 for (TQ_INT32 i = yradius; i > 0; i--)
869 {
870 max[x][i] = MAX (MAX (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
871 }
872 max[x][0] = buf[0][x];
873 }
874 TQ_INT32 last_max = max[0][circ[-1]];
875 TQ_INT32 last_index = 1;
876 for (TQ_INT32 x = 0; x < layerSize.width(); x++) /* render scan line */
877 {
878 last_index--;
879 if (last_index >= 0)
880 {
881 if (last_max == 255)
882 out[x] = 255;
883 else
884 {
885 last_max = 0;
886 for (TQ_INT32 i = xradius; i >= 0; i--)
887 if (last_max < max[x + i][circ[i]])
888 {
889 last_max = max[x + i][circ[i]];
890 last_index = i;
891 }
892 out[x] = last_max;
893 }
894 }
895 else
896 {
897 last_index = xradius;
898 last_max = max[x + xradius][circ[xradius]];
899 for (TQ_INT32 i = xradius - 1; i >= -xradius; i--)
900 if (last_max < max[x + i][circ[i]])
901 {
902 last_max = max[x + i][circ[i]];
903 last_index = i;
904 }
905 out[x] = last_max;
906 }
907 }
908 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
909 }
910 /* undo the offsets to the pointers so we can free the malloced memmory */
911 circ -= xradius;
912 max -= xradius;
913 //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
914 delete[] circ;
915 delete[] buffer;
916 delete[] max;
917 for (TQ_INT32 i = 0; i < yradius + 1; i++)
918 delete[] buf[i];
919 delete[] buf;
920 delete[] out;
921
922 dev->setDirty();
923 dev->emitSelectionChanged();
924
925 if (t) {
926 img->undoAdapter()->addCommand(t);
927 }
928}
929
930void KisSelectionManager::shrink (TQ_INT32 xradius, TQ_INT32 yradius, bool edge_lock)
931{
932
933 KisImageSP img = m_parent->currentImg();
934 if (!img) return;
935
936 KisPaintDeviceSP dev = img->activeDevice();
937 if (!dev) return;
938
939 if (!dev->hasSelection()) return;
940 KisSelectionSP selection = dev->selection();
941
942 //determine the layerSize
943 TQRect layerSize = dev->exactBounds();
944 /*
945 pretty much the same as fatten_region only different
946 blame all bugs in this function on jaycox@gimp.org
947 */
948 /* If edge_lock is true we assume that pixels outside the region
949 we are passed are identical to the edge pixels.
950 If edge_lock is false, we assume that pixels outside the region are 0
951 */
952 TQ_UINT8 **buf; // caches the the region's pixels
953 TQ_UINT8 **max; // caches the smallest values for each column
954 TQ_INT32 last_max, last_index;
955
956 if (xradius <= 0 || yradius <= 0)
957 return;
958
959 max = new TQ_UINT8* [layerSize.width() + 2 * xradius];
960 buf = new TQ_UINT8* [yradius + 1];
961 for (TQ_INT32 i = 0; i < yradius + 1; i++)
962 {
963 buf[i] = new TQ_UINT8[layerSize.width()];
964 }
965
966 TQ_INT32 buffer_size = (layerSize.width() + 2 * xradius + 1) * (yradius + 1);
967 TQ_UINT8* buffer = new TQ_UINT8[buffer_size];
968
969 if (edge_lock)
970 memset(buffer, 255, buffer_size);
971 else
972 memset(buffer, 0, buffer_size);
973
974 for (TQ_INT32 i = 0; i < layerSize.width() + 2 * xradius; i++)
975 {
976 if (i < xradius)
977 if (edge_lock)
978 max[i] = buffer;
979 else
980 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
981 else if (i < layerSize.width() + xradius)
982 max[i] = &buffer[(yradius + 1) * (i - xradius)];
983 else
984 if (edge_lock)
985 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius - 1)];
986 else
987 max[i] = &buffer[(yradius + 1) * (layerSize.width() + xradius)];
988 }
989 if (!edge_lock)
990 for (TQ_INT32 j = 0 ; j < xradius + 1; j++) max[0][j] = 0;
991
992 // offset the max pointer by xradius so the range of the array is [-xradius] to [region->w + xradius]
993 max += xradius;
994
995 TQ_UINT8* out = new TQ_UINT8[layerSize.width()]; // holds the new scan line we are computing
996
997 TQ_INT32* circ = new TQ_INT32[2 * xradius + 1]; // holds the y coords of the filter's mask
998
999 computeBorder (circ, xradius, yradius);
1000
1001 // offset the circ pointer by xradius so the range of the array is [-xradius] to [xradius]
1002 circ += xradius;
1003
1004 for (TQ_INT32 i = 0; i < yradius && i < layerSize.height(); i++) // load top of image
1005 selection->readBytes(buf[i + 1], layerSize.x(), layerSize.y() + i, layerSize.width(), 1);
1006
1007 if (edge_lock)
1008 memcpy (buf[0], buf[1], layerSize.width());
1009 else
1010 memset (buf[0], 0, layerSize.width());
1011
1012
1013 for (TQ_INT32 x = 0; x < layerSize.width(); x++) // set up max for top of image
1014 {
1015 max[x][0] = buf[0][x];
1016 for (TQ_INT32 j = 1; j < yradius + 1; j++)
1017 max[x][j] = MIN(buf[j][x], max[x][j-1]);
1018 }
1019
1020 for (TQ_INT32 y = 0; y < layerSize.height(); y++)
1021 {
1022 rotatePointers (buf, yradius + 1);
1023 if (y < layerSize.height() - yradius)
1024 selection->readBytes(buf[yradius], layerSize.x(), layerSize.y() + y + yradius, layerSize.width(), 1);
1025 else if (edge_lock)
1026 memcpy (buf[yradius], buf[yradius - 1], layerSize.width());
1027 else
1028 memset (buf[yradius], 0, layerSize.width());
1029
1030 for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // update max array
1031 {
1032 for (TQ_INT32 i = yradius; i > 0; i--)
1033 {
1034 max[x][i] = MIN (MIN (max[x][i - 1], buf[i - 1][x]), buf[i][x]);
1035 }
1036 max[x][0] = buf[0][x];
1037 }
1038 last_max = max[0][circ[-1]];
1039 last_index = 0;
1040
1041 for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
1042 {
1043 last_index--;
1044 if (last_index >= 0)
1045 {
1046 if (last_max == 0)
1047 out[x] = 0;
1048 else
1049 {
1050 last_max = 255;
1051 for (TQ_INT32 i = xradius; i >= 0; i--)
1052 if (last_max > max[x + i][circ[i]])
1053 {
1054 last_max = max[x + i][circ[i]];
1055 last_index = i;
1056 }
1057 out[x] = last_max;
1058 }
1059 }
1060 else
1061 {
1062 last_index = xradius;
1063 last_max = max[x + xradius][circ[xradius]];
1064 for (TQ_INT32 i = xradius - 1; i >= -xradius; i--)
1065 if (last_max > max[x + i][circ[i]])
1066 {
1067 last_max = max[x + i][circ[i]];
1068 last_index = i;
1069 }
1070 out[x] = last_max;
1071 }
1072 }
1073 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
1074 }
1075
1076 // undo the offsets to the pointers so we can free the malloced memmory
1077 circ -= xradius;
1078 max -= xradius;
1079 //free the memmory
1080 //XXXX: replace delete by delete[] where it is necessary to avoid memory leaks!
1081 delete[] circ;
1082 delete[] buffer;
1083 delete[] max;
1084 for (TQ_INT32 i = 0; i < yradius + 1; i++)
1085 delete buf[i];
1086 delete[] buf;
1087 delete[] out;
1088
1089 dev->setDirty(layerSize);
1090 dev->emitSelectionChanged();
1091}
1092
1093//Simple convolution filter to smooth a mask (1bpp)
1094
1095void KisSelectionManager::smooth()
1096{
1097 KisImageSP img = m_parent->currentImg();
1098 if (!img) return;
1099
1100 KisPaintDeviceSP dev = img->activeDevice();
1101 if (!dev) return;
1102
1103 if (!dev->hasSelection()) return;
1104 KisSelectionSP selection = dev->selection();
1105
1106 //determine the layerSize
1107 TQRect layerSize = dev->exactBounds();
1108
1109 TQ_UINT8 *buf[3];
1110
1111 TQ_INT32 width = layerSize.width();
1112
1113 for (TQ_INT32 i = 0; i < 3; i++) buf[i] = new TQ_UINT8[width + 2];
1114
1115 TQ_UINT8* out = new TQ_UINT8[width];
1116
1117 // load top of image
1118 selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
1119
1120 buf[0][0] = buf[0][1];
1121 buf[0][width + 1] = buf[0][width];
1122
1123 memcpy (buf[1], buf[0], width + 2);
1124
1125 for (TQ_INT32 y = 0; y < layerSize.height(); y++)
1126 {
1127 if (y + 1 < layerSize.height())
1128 {
1129 selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
1130
1131 buf[2][0] = buf[2][1];
1132 buf[2][width + 1] = buf[2][width];
1133 }
1134 else
1135 {
1136 memcpy (buf[2], buf[1], width + 2);
1137 }
1138
1139 for (TQ_INT32 x = 0 ; x < width; x++)
1140 {
1141 TQ_INT32 value = (buf[0][x] + buf[0][x+1] + buf[0][x+2] +
1142 buf[1][x] + buf[2][x+1] + buf[1][x+2] +
1143 buf[2][x] + buf[1][x+1] + buf[2][x+2]);
1144
1145 out[x] = value / 9;
1146 }
1147
1148 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
1149
1150 rotatePointers (buf, 3);
1151 }
1152
1153 for (TQ_INT32 i = 0; i < 3; i++)
1154 delete[] buf[i];
1155
1156 delete[] out;
1157
1158 dev->setDirty();
1159 dev->emitSelectionChanged();
1160}
1161
1162// Erode (radius 1 pixel) a mask (1bpp)
1163
1164void KisSelectionManager::erode()
1165{
1166 KisImageSP img = m_parent->currentImg();
1167 if (!img) return;
1168
1169 KisPaintDeviceSP dev = img->activeDevice();
1170 if (!dev) return;
1171
1172 if (!dev->hasSelection()) return;
1173 KisSelectionSP selection = dev->selection();
1174
1175 //determine the layerSize
1176 TQRect layerSize = dev->exactBounds();
1177
1178 TQ_UINT8* buf[3];
1179
1180
1181 TQ_INT32 width = layerSize.width();
1182
1183 for (TQ_INT32 i = 0; i < 3; i++)
1184 buf[i] = new TQ_UINT8[width + 2];
1185
1186 TQ_UINT8* out = new TQ_UINT8[width];
1187
1188 // load top of image
1189 selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
1190
1191 buf[0][0] = buf[0][1];
1192 buf[0][width + 1] = buf[0][width];
1193
1194 memcpy (buf[1], buf[0], width + 2);
1195
1196 for (TQ_INT32 y = 0; y < layerSize.height(); y++)
1197 {
1198 if (y + 1 < layerSize.height())
1199 {
1200 selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
1201
1202 buf[2][0] = buf[2][1];
1203 buf[2][width + 1] = buf[2][width];
1204 }
1205 else
1206 {
1207 memcpy (buf[2], buf[1], width + 2);
1208 }
1209
1210 for (TQ_INT32 x = 0 ; x < width; x++)
1211 {
1212 TQ_INT32 min = 255;
1213
1214 if (buf[0][x+1] < min) min = buf[0][x+1];
1215 if (buf[1][x] < min) min = buf[1][x];
1216 if (buf[1][x+1] < min) min = buf[1][x+1];
1217 if (buf[1][x+2] < min) min = buf[1][x+2];
1218 if (buf[2][x+1] < min) min = buf[2][x+1];
1219
1220 out[x] = min;
1221 }
1222
1223 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
1224
1225 rotatePointers (buf, 3);
1226 }
1227
1228 for (TQ_INT32 i = 0; i < 3; i++)
1229 delete[] buf[i];
1230
1231 delete[] out;
1232
1233 dev->setDirty();
1234 dev->emitSelectionChanged();
1235}
1236
1237// dilate (radius 1 pixel) a mask (1bpp)
1238
1239void KisSelectionManager::dilate()
1240{
1241 KisImageSP img = m_parent->currentImg();
1242 if (!img) return;
1243
1244 KisPaintDeviceSP dev = img->activeDevice();
1245 if (!dev) return;
1246
1247 if (!dev->hasSelection()) return;
1248 KisSelectionSP selection = dev->selection();
1249
1250 //determine the layerSize
1251 TQRect layerSize = dev->exactBounds();
1252
1253 TQ_UINT8* buf[3];
1254
1255 TQ_INT32 width = layerSize.width();
1256
1257 for (TQ_INT32 i = 0; i < 3; i++)
1258 buf[i] = new TQ_UINT8[width + 2];
1259
1260 TQ_UINT8* out = new TQ_UINT8[width];
1261
1262 // load top of image
1263 selection->readBytes(buf[0] + 1, layerSize.x(), layerSize.y(), width, 1);
1264
1265 buf[0][0] = buf[0][1];
1266 buf[0][width + 1] = buf[0][width];
1267
1268 memcpy (buf[1], buf[0], width + 2);
1269
1270 for (TQ_INT32 y = 0; y < layerSize.height(); y++)
1271 {
1272 if (y + 1 < layerSize.height())
1273 {
1274 selection->readBytes(buf[2] + 1, layerSize.x(), layerSize.y() + y + 1, width, 1);
1275
1276 buf[2][0] = buf[2][1];
1277 buf[2][width + 1] = buf[2][width];
1278 }
1279 else
1280 {
1281 memcpy (buf[2], buf[1], width + 2);
1282 }
1283
1284 for (TQ_INT32 x = 0 ; x < width; x++)
1285 {
1286 TQ_INT32 max = 0;
1287
1288 if (buf[0][x+1] > max) max = buf[0][x+1];
1289 if (buf[1][x] > max) max = buf[1][x];
1290 if (buf[1][x+1] > max) max = buf[1][x+1];
1291 if (buf[1][x+2] > max) max = buf[1][x+2];
1292 if (buf[2][x+1] > max) max = buf[2][x+1];
1293
1294 out[x] = max;
1295 }
1296
1297 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, width, 1);
1298
1299 rotatePointers (buf, 3);
1300 }
1301
1302 for (TQ_INT32 i = 0; i < 3; i++)
1303 delete[] buf[i];
1304
1305 delete[] out;
1306
1307 dev->setDirty();
1308 dev->emitSelectionChanged();
1309}
1310
1311void KisSelectionManager::border(TQ_INT32 xradius, TQ_INT32 yradius)
1312{
1313 KisImageSP img = m_parent->currentImg();
1314 if (!img) return;
1315
1316 KisPaintDeviceSP dev = img->activeDevice();
1317 if (!dev) return;
1318
1319 if (!dev->hasSelection()) return;
1320 KisSelectionSP selection = dev->selection();
1321
1322 //determine the layerSize
1323 TQRect layerSize = dev->exactBounds();
1324
1325 /*
1326 This function has no bugs, but if you imagine some you can
1327 blame them on jaycox@gimp.org
1328 */
1329 TQ_UINT8 *buf[3];
1330 TQ_UINT8 **density;
1331 TQ_UINT8 **transition;
1332
1333 if (xradius == 1 && yradius == 1) // optimize this case specifically
1334 {
1335 TQ_UINT8* source[3];
1336
1337 for (TQ_INT32 i = 0; i < 3; i++)
1338 source[i] = new TQ_UINT8[layerSize.width()];
1339
1340 TQ_UINT8* transition = new TQ_UINT8[layerSize.width()];
1341
1342 selection->readBytes(source[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
1343 memcpy (source[1], source[0], layerSize.width());
1344 if (layerSize.height() > 1)
1345 selection->readBytes(source[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
1346 else
1347 memcpy (source[2], source[1], layerSize.width());
1348
1349 computeTransition (transition, source, layerSize.width());
1350 selection->writeBytes(transition, layerSize.x(), layerSize.y(), layerSize.width(), 1);
1351
1352 for (TQ_INT32 y = 1; y < layerSize.height(); y++)
1353 {
1354 rotatePointers (source, 3);
1355 if (y + 1 < layerSize.height())
1356 selection->readBytes(source[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
1357 else
1358 memcpy(source[2], source[1], layerSize.width());
1359 computeTransition (transition, source, layerSize.width());
1360 selection->writeBytes(transition, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
1361 }
1362
1363 for (TQ_INT32 i = 0; i < 3; i++)
1364 delete[] source[i];
1365 delete[] transition;
1366 return;
1367 }
1368
1369 TQ_INT32* max = new TQ_INT32[layerSize.width() + 2 * xradius];
1370 for (TQ_INT32 i = 0; i < (layerSize.width() + 2 * xradius); i++)
1371 max[i] = yradius + 2;
1372 max += xradius;
1373
1374 for (TQ_INT32 i = 0; i < 3; i++)
1375 buf[i] = new TQ_UINT8[layerSize.width()];
1376
1377 transition = new TQ_UINT8*[yradius + 1];
1378 for (TQ_INT32 i = 0; i < yradius + 1; i++)
1379 {
1380 transition[i] = new TQ_UINT8[layerSize.width() + 2 * xradius];
1381 memset(transition[i], 0, layerSize.width() + 2 * xradius);
1382 transition[i] += xradius;
1383 }
1384 TQ_UINT8* out = new TQ_UINT8[layerSize.width()];
1385 density = new TQ_UINT8*[2 * xradius + 1];
1386 density += xradius;
1387
1388 for (TQ_INT32 x = 0; x < (xradius + 1); x++) // allocate density[][]
1389 {
1390 density[ x] = new TQ_UINT8[2 * yradius + 1];
1391 density[ x] += yradius;
1392 density[-x] = density[x];
1393 }
1394 for (TQ_INT32 x = 0; x < (xradius + 1); x++) // compute density[][]
1395 {
1396 double tmpx, tmpy, dist;
1397 TQ_UINT8 a;
1398
1399 if (x > 0)
1400 tmpx = x - 0.5;
1401 else if (x < 0)
1402 tmpx = x + 0.5;
1403 else
1404 tmpx = 0.0;
1405
1406 for (TQ_INT32 y = 0; y < (yradius + 1); y++)
1407 {
1408 if (y > 0)
1409 tmpy = y - 0.5;
1410 else if (y < 0)
1411 tmpy = y + 0.5;
1412 else
1413 tmpy = 0.0;
1414 dist = ((tmpy * tmpy) / (yradius * yradius) +
1415 (tmpx * tmpx) / (xradius * xradius));
1416 if (dist < 1.0)
1417 a = 255 * (TQ_UINT8)(1.0 - sqrt (dist));
1418 else
1419 a = 0;
1420 density[ x][ y] = a;
1421 density[ x][-y] = a;
1422 density[-x][ y] = a;
1423 density[-x][-y] = a;
1424 }
1425 }
1426 selection->readBytes(buf[0], layerSize.x(), layerSize.y(), layerSize.width(), 1);
1427 memcpy (buf[1], buf[0], layerSize.width());
1428 if (layerSize.height() > 1)
1429 selection->readBytes(buf[2], layerSize.x(), layerSize.y() + 1, layerSize.width(), 1);
1430 else
1431 memcpy (buf[2], buf[1], layerSize.width());
1432 computeTransition (transition[1], buf, layerSize.width());
1433
1434 for (TQ_INT32 y = 1; y < yradius && y + 1 < layerSize.height(); y++) // set up top of image
1435 {
1436 rotatePointers (buf, 3);
1437 selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + 1, layerSize.width(), 1);
1438 computeTransition (transition[y + 1], buf, layerSize.width());
1439 }
1440 for (TQ_INT32 x = 0; x < layerSize.width(); x++) // set up max[] for top of image
1441 {
1442 max[x] = -(yradius + 7);
1443 for (TQ_INT32 j = 1; j < yradius + 1; j++)
1444 if (transition[j][x])
1445 {
1446 max[x] = j;
1447 break;
1448 }
1449 }
1450 for (TQ_INT32 y = 0; y < layerSize.height(); y++) // main calculation loop
1451 {
1452 rotatePointers (buf, 3);
1453 rotatePointers (transition, yradius + 1);
1454 if (y < layerSize.height() - (yradius + 1))
1455 {
1456 selection->readBytes(buf[2], layerSize.x(), layerSize.y() + y + yradius + 1, layerSize.width(), 1);
1457 computeTransition (transition[yradius], buf, layerSize.width());
1458 }
1459 else
1460 memcpy (transition[yradius], transition[yradius - 1], layerSize.width());
1461
1462 for (TQ_INT32 x = 0; x < layerSize.width(); x++) // update max array
1463 {
1464 if (max[x] < 1)
1465 {
1466 if (max[x] <= -yradius)
1467 {
1468 if (transition[yradius][x])
1469 max[x] = yradius;
1470 else
1471 max[x]--;
1472 }
1473 else
1474 if (transition[-max[x]][x])
1475 max[x] = -max[x];
1476 else if (transition[-max[x] + 1][x])
1477 max[x] = -max[x] + 1;
1478 else
1479 max[x]--;
1480 }
1481 else
1482 max[x]--;
1483 if (max[x] < -yradius - 1)
1484 max[x] = -yradius - 1;
1485 }
1486 TQ_UINT8 last_max = max[0][density[-1]];
1487 TQ_INT32 last_index = 1;
1488 for (TQ_INT32 x = 0 ; x < layerSize.width(); x++) // render scan line
1489 {
1490 last_index--;
1491 if (last_index >= 0)
1492 {
1493 last_max = 0;
1494 for (TQ_INT32 i = xradius; i >= 0; i--)
1495 if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x+i]] > last_max)
1496 {
1497 last_max = density[i][max[x + i]];
1498 last_index = i;
1499 }
1500 out[x] = last_max;
1501 }
1502 else
1503 {
1504 last_max = 0;
1505 for (TQ_INT32 i = xradius; i >= -xradius; i--)
1506 if (max[x + i] <= yradius && max[x + i] >= -yradius && density[i][max[x + i]] > last_max)
1507 {
1508 last_max = density[i][max[x + i]];
1509 last_index = i;
1510 }
1511 out[x] = last_max;
1512 }
1513 if (last_max == 0)
1514 {
1515 TQ_INT32 i;
1516 for (i = x + 1; i < layerSize.width(); i++)
1517 {
1518 if (max[i] >= -yradius)
1519 break;
1520 }
1521 if (i - x > xradius)
1522 {
1523 for (; x < i - xradius; x++)
1524 out[x] = 0;
1525 x--;
1526 }
1527 last_index = xradius;
1528 }
1529 }
1530 selection->writeBytes(out, layerSize.x(), layerSize.y() + y, layerSize.width(), 1);
1531 }
1532 delete [] out;
1533
1534 for (TQ_INT32 i = 0; i < 3; i++)
1535 delete buf[i];
1536
1537 max -= xradius;
1538 delete[] max;
1539
1540 for (TQ_INT32 i = 0; i < yradius + 1; i++)
1541 {
1542 transition[i] -= xradius;
1543 delete transition[i];
1544 }
1545 delete[] transition;
1546
1547 for (TQ_INT32 i = 0; i < xradius + 1 ; i++)
1548 {
1549 density[i] -= yradius;
1550 delete density[i];
1551 }
1552 density -= xradius;
1553 delete[] density;
1554
1555 dev->setDirty();
1556 dev->emitSelectionChanged();
1557}
1558
1559#define RINT(x) floor ((x) + 0.5)
1560
1561void KisSelectionManager::computeBorder (TQ_INT32 *circ, TQ_INT32 xradius, TQ_INT32 yradius)
1562{
1563 Q_ASSERT(xradius != 0);
1564 TQ_INT32 i;
1565 TQ_INT32 diameter = xradius * 2 + 1;
1566 double tmp;
1567
1568 for (i = 0; i < diameter; i++)
1569 {
1570 if (i > xradius)
1571 tmp = (i - xradius) - 0.5;
1572 else if (i < xradius)
1573 tmp = (xradius - i) - 0.5;
1574 else
1575 tmp = 0.0;
1576
1577 circ[i] = (TQ_INT32) RINT (yradius / (double) xradius * sqrt (xradius * xradius - tmp * tmp));
1578 }
1579}
1580
1581void KisSelectionManager::rotatePointers (TQ_UINT8 **p, TQ_UINT32 n)
1582{
1583 TQ_UINT32 i;
1584 TQ_UINT8 *tmp;
1585
1586 tmp = p[0];
1587
1588 for (i = 0; i < n - 1; i++) p[i] = p[i + 1];
1589
1590 p[i] = tmp;
1591}
1592
1593void KisSelectionManager::computeTransition (TQ_UINT8* transition, TQ_UINT8** buf, TQ_INT32 width)
1594{
1595 TQ_INT32 x = 0;
1596
1597 if (width == 1)
1598 {
1599 if (buf[1][x] > 127 && (buf[0][x] < 128 || buf[2][x] < 128))
1600 transition[x] = 255;
1601 else
1602 transition[x] = 0;
1603 return;
1604 }
1605 if (buf[1][x] > 127)
1606 {
1607 if ( buf[0][x] < 128 || buf[0][x + 1] < 128 ||
1608 buf[1][x + 1] < 128 ||
1609 buf[2][x] < 128 || buf[2][x + 1] < 128 )
1610 transition[x] = 255;
1611 else
1612 transition[x] = 0;
1613 }
1614 else
1615 transition[x] = 0;
1616 for (TQ_INT32 x = 1; x < width - 1; x++)
1617 {
1618 if (buf[1][x] >= 128)
1619 {
1620 if (buf[0][x - 1] < 128 || buf[0][x] < 128 || buf[0][x + 1] < 128 ||
1621 buf[1][x - 1] < 128 || buf[1][x + 1] < 128 ||
1622 buf[2][x - 1] < 128 || buf[2][x] < 128 || buf[2][x + 1] < 128)
1623 transition[x] = 255;
1624 else
1625 transition[x] = 0;
1626 }
1627 else
1628 transition[x] = 0;
1629 }
1630 if (buf[1][x] >= 128)
1631 {
1632 if (buf[0][x - 1] < 128 || buf[0][x] < 128 ||
1633 buf[1][x - 1] < 128 ||
1634 buf[2][x - 1] < 128 || buf[2][x] < 128)
1635 transition[x] = 255;
1636 else
1637 transition[x] = 0;
1638 }
1639 else
1640 transition[x] = 0;
1641}
1642
1643#include "kis_selection_manager.moc"
void clearSelection()
Clears the selection (doesn't call any of the update or dirty methods).
virtual TQPoint viewToWindow(const TQPoint &pt)=0
Conversion functions from view coordinates to image coordinates.
virtual KisCanvas * kiscanvas() const =0
virtual void applyAlphaU8Mask(TQ_UINT8 *pixels, TQ_UINT8 *alpha, TQ_INT32 nPixels)=0
Applies the specified 8-bit alpha mask to the pixels.
A KisColor describes a color in a certain colorspace.
Definition kis_color.h:33
virtual void setDirty(bool propagate=true)
Set the entire layer extent dirty; this percolates up to parent layers all the way to the root layer.
KCommand * end()
Finish painting on the current device.
void bitBlt(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
Blast the specified region from src onto the current paint device.
void begin(KisPaintDeviceSP device)
Start painting on the specified device.
bool displaySelection()
This function return if the selection should be displayed.