File indexing completed on 2024-05-19 04:29:07
0001 /* 0002 * SPDX-FileCopyrightText: 2008 Sven Langkamp <sven.langkamp@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kis_selection_decoration.h" 0008 0009 #include <QPainter> 0010 #include <QVarLengthArray> 0011 #include <QApplication> 0012 #include <QMainWindow> 0013 #include <QWindow> 0014 #include <QScreen> 0015 0016 #include <kis_debug.h> 0017 #include <klocalizedstring.h> 0018 0019 #include <KisMainWindow.h> 0020 #include "kis_types.h" 0021 #include "KisViewManager.h" 0022 #include "kis_selection.h" 0023 #include "kis_image.h" 0024 #include "flake/kis_shape_selection.h" 0025 #include "kis_pixel_selection.h" 0026 #include "kis_update_outline_job.h" 0027 #include "kis_selection_manager.h" 0028 #include "canvas/kis_canvas2.h" 0029 #include "kis_canvas_resource_provider.h" 0030 #include "kis_coordinates_converter.h" 0031 #include "kis_config.h" 0032 #include "kis_config_notifier.h" 0033 #include "kis_image_config.h" 0034 #include "KisImageConfigNotifier.h" 0035 #include "kis_painting_tweaks.h" 0036 #include "KisView.h" 0037 #include "kis_selection_mask.h" 0038 #include <KisPart.h> 0039 #include <KisScreenMigrationTracker.h> 0040 0041 static const unsigned int ANT_LENGTH = 4; 0042 static const unsigned int ANT_SPACE = 4; 0043 static const unsigned int ANT_ADVANCE_WIDTH = ANT_LENGTH + ANT_SPACE; 0044 0045 KisSelectionDecoration::KisSelectionDecoration(QPointer<KisView>_view) 0046 : KisCanvasDecoration("selection", _view), 0047 m_signalCompressor(50 /*ms*/, KisSignalCompressor::FIRST_ACTIVE), 0048 m_offset(0), 0049 m_mode(Ants) 0050 { 0051 initializePens(); 0052 connect(this->view()->canvasBase()->resourceManager(), SIGNAL(canvasResourceChanged(int, const QVariant&)), this, SLOT(slotCanvasResourcesChanged(int, const QVariant&))); 0053 0054 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); 0055 connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); 0056 slotConfigChanged(); 0057 0058 m_antsTimer = new QTimer(this); 0059 m_antsTimer->setInterval(150); 0060 m_antsTimer->setSingleShot(false); 0061 connect(m_antsTimer, SIGNAL(timeout()), SLOT(antsAttackEvent())); 0062 0063 connect(&m_signalCompressor, SIGNAL(timeout()), SLOT(slotStartUpdateSelection())); 0064 0065 // selections should be at the top of the stack 0066 setPriority(100); 0067 } 0068 0069 KisSelectionDecoration::~KisSelectionDecoration() 0070 { 0071 } 0072 0073 KisSelectionDecoration::Mode KisSelectionDecoration::mode() const 0074 { 0075 return m_mode; 0076 } 0077 0078 void KisSelectionDecoration::setMode(Mode mode) 0079 { 0080 m_mode = mode; 0081 selectionChanged(); 0082 } 0083 0084 bool KisSelectionDecoration::selectionIsActive() 0085 { 0086 KisSelectionSP selection = view()->selection(); 0087 return visible() && selection && 0088 (selection->hasNonEmptyPixelSelection() || selection->hasNonEmptyShapeSelection()) && 0089 selection->isVisible(); 0090 } 0091 0092 void KisSelectionDecoration::initializePens() 0093 { 0094 KisPaintingTweaks::initAntsPen(&m_antsPen, &m_outlinePen, 0095 ANT_LENGTH, ANT_SPACE); 0096 0097 m_antsPen.setWidth(decorationThickness()); 0098 m_outlinePen.setWidth(decorationThickness()); 0099 } 0100 0101 void KisSelectionDecoration::selectionChanged() 0102 { 0103 KisSelectionMaskSP mask = qobject_cast<KisSelectionMask*>(view()->currentNode().data()); 0104 if (!mask || !mask->active() || !mask->visible(true)) { 0105 mask = 0; 0106 } 0107 0108 if (!view()->isCurrent() || 0109 view()->viewManager()->mainWindow() == KisPart::instance()->currentMainwindow()) { 0110 0111 view()->image()->setOverlaySelectionMask(mask); 0112 } 0113 0114 KisSelectionSP selection = view()->selection(); 0115 0116 if (!mask && selection && selectionIsActive()) { 0117 if ((m_mode == Ants && selection->outlineCacheValid()) || 0118 (m_mode == Mask && selection->thumbnailImageValid())) { 0119 0120 m_signalCompressor.stop(); 0121 0122 if (m_mode == Ants) { 0123 m_outlinePath = selection->outlineCache(); 0124 m_antsTimer->start(); 0125 } else { 0126 m_thumbnailImage = selection->thumbnailImage(); 0127 m_thumbnailImageTransform = selection->thumbnailImageTransform(); 0128 m_antsTimer->stop(); 0129 } 0130 if (view() && view()->canvasBase()) { 0131 view()->canvasBase()->updateCanvas(); 0132 } 0133 0134 } else { 0135 m_signalCompressor.start(); 0136 } 0137 } else { 0138 m_signalCompressor.stop(); 0139 m_outlinePath = QPainterPath(); 0140 m_thumbnailImage = QImage(); 0141 m_thumbnailImageTransform = QTransform(); 0142 view()->canvasBase()->updateCanvas(); 0143 m_antsTimer->stop(); 0144 } 0145 } 0146 0147 void KisSelectionDecoration::slotStartUpdateSelection() 0148 { 0149 KisSelectionSP selection = view()->selection(); 0150 if (!selection) return; 0151 0152 view()->image()->addSpontaneousJob(new KisUpdateOutlineJob(selection, m_mode == Mask, m_maskColor)); 0153 } 0154 0155 void KisSelectionDecoration::slotConfigChanged() 0156 { 0157 KisImageConfig imageConfig(true); 0158 KisConfig cfg(true); 0159 0160 m_opacity = imageConfig.selectionOutlineOpacity(); 0161 m_maskColor = imageConfig.selectionOverlayMaskColor(); 0162 m_antialiasSelectionOutline = cfg.antialiasSelectionOutline(); 0163 } 0164 0165 void KisSelectionDecoration::slotCanvasResourcesChanged(int key, const QVariant &v) 0166 { 0167 Q_UNUSED(v); 0168 if (key == KoCanvasResource::DecorationThickness) { 0169 initializePens(); 0170 } 0171 } 0172 0173 void KisSelectionDecoration::antsAttackEvent() 0174 { 0175 KisSelectionSP selection = view()->selection(); 0176 if (!selection) return; 0177 0178 if (selectionIsActive()) { 0179 m_offset = (m_offset + 1) % (ANT_ADVANCE_WIDTH); 0180 m_antsPen.setDashOffset(m_offset); 0181 view()->canvasBase()->updateCanvas(); 0182 } 0183 } 0184 0185 void KisSelectionDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, KisCanvas2 *canvas) 0186 { 0187 Q_UNUSED(updateRect); 0188 Q_UNUSED(canvas); 0189 0190 if (!selectionIsActive()) return; 0191 if ((m_mode == Ants && m_outlinePath.isEmpty()) || 0192 (m_mode == Mask && m_thumbnailImage.isNull())) return; 0193 0194 QTransform transform = converter->imageToWidgetTransform(); 0195 0196 gc.save(); 0197 gc.setTransform(transform, false); 0198 0199 if (m_mode == Mask) { 0200 gc.setRenderHints(QPainter::SmoothPixmapTransform | 0201 QPainter::Antialiasing, false); 0202 0203 gc.setTransform(m_thumbnailImageTransform, true); 0204 gc.drawImage(QPoint(), m_thumbnailImage); 0205 0206 QRect r1 = m_thumbnailImageTransform.inverted().mapRect(view()->image()->bounds()); 0207 QRect r2 = m_thumbnailImage.rect(); 0208 0209 QPainterPath p1; 0210 p1.addRect(r1); 0211 0212 QPainterPath p2; 0213 p2.addRect(r2); 0214 0215 gc.setBrush(m_maskColor); 0216 gc.setPen(Qt::NoPen); 0217 gc.drawPath(p1 - p2); 0218 0219 } else /* if (m_mode == Ants) */ { 0220 gc.setRenderHints(QPainter::Antialiasing | QPainter::Antialiasing, m_antialiasSelectionOutline); 0221 0222 gc.setOpacity(m_opacity); 0223 0224 // render selection outline in white 0225 gc.setPen(m_outlinePen); 0226 gc.drawPath(m_outlinePath); 0227 0228 // render marching ants in black (above the white outline) 0229 gc.setPen(m_antsPen); 0230 gc.drawPath(m_outlinePath); 0231 } 0232 gc.restore(); 0233 } 0234 0235 void KisSelectionDecoration::setVisible(bool v) 0236 { 0237 KisCanvasDecoration::setVisible(v); 0238 selectionChanged(); 0239 } 0240 0241 void KisSelectionDecoration::notifyWindowMinimized(bool minimized) 0242 { 0243 if(minimized) { 0244 m_antsTimer->stop(); 0245 } else { 0246 selectionChanged(); 0247 } 0248 }