From kde-kimageshop Sun Sep 09 13:52:43 2018 From: Dmitry Kazakov Date: Sun, 09 Sep 2018 13:52:43 +0000 To: kde-kimageshop Subject: [krita] /: FEATURE: actions for rotate/scale/mirror/shear selection Message-Id: X-MARC-Message: https://marc.info/?l=kde-kimageshop&m=153650549721045 Git commit 3014d62c9817690ed21384450cb771c75bf60ce4 by Dmitry Kazakov. Committed on 09/09/2018 at 13:52. Pushed by dkazakov into branch 'master'. FEATURE: actions for rotate/scale/mirror/shear selection Now Rotate/Scale/Mirror/Shear Layer actions also support handling a selection. If there is a selection active, then the action will transform selected content only. Now there are also 8 new actions, that will rotate/scale/ mirror/shear *all* layers without resizing the image. These actions can also handle selections. Animation Note: * when no selection present, the actions transform *all* the frames of the layers in question * when there is a selection active, only current frame is transformed BUG:365595 Ref T3920 CC:kimageshop@kde.org M +96 -0 krita/krita.action M +15 -0 krita/krita4.xmlgui M +2 -0 libs/image/CMakeLists.txt R +0 -0 libs/image/commands_new/kis_transaction_based_command.cpp [f= rom: libs/ui/kis_transaction_based_command.cpp - 100% similarity] R +2 -2 libs/image/commands_new/kis_transaction_based_command.h [fro= m: libs/ui/kis_transaction_based_command.h - 092% similarity] M +79 -27 libs/image/kis_image.cc M +6 -7 libs/image/kis_image.h M +12 -0 libs/image/kis_processing_applicator.cpp M +5 -0 libs/image/kis_processing_visitor.cpp M +8 -0 libs/image/kis_processing_visitor.h A +91 -0 libs/image/processing/KisSelectionBasedProcessingHelper.cpp = [License: UNKNOWN] * A +37 -0 libs/image/processing/KisSelectionBasedProcessingHelper.h = [License: UNKNOWN] * M +26 -9 libs/image/processing/kis_mirror_processing_visitor.cpp M +10 -0 libs/image/processing/kis_mirror_processing_visitor.h M +37 -5 libs/image/processing/kis_transform_processing_visitor.cpp M +8 -1 libs/image/processing/kis_transform_processing_visitor.h M +11 -4 libs/libkis/Node.cpp M +1 -1 libs/libkis/Node.h M +0 -1 libs/ui/CMakeLists.txt M +1 -1 libs/ui/actions/kis_selection_action_factories.cpp M +42 -56 libs/ui/kis_node_manager.cpp M +7 -12 libs/ui/kis_node_manager.h M +1 -1 libs/ui/operations/kis_filter_selection_operation.cpp M +0 -104 libs/ui/tests/kis_node_manager_test.cpp M +0 -6 libs/ui/tests/kis_node_manager_test.h M +1 -1 libs/ui/tool/kis_selection_tool_helper.cpp M +1 -1 plugins/dockers/gamutmask/gamutmask_dock.cpp M +46 -14 plugins/extensions/imagesize/imagesize.cc M +6 -0 plugins/extensions/imagesize/imagesize.h M +1 -1 plugins/extensions/pykrita/sip/krita/Node.sip M +91 -10 plugins/extensions/rotateimage/rotateimage.cc M +12 -0 plugins/extensions/rotateimage/rotateimage.h M +25 -4 plugins/extensions/shearimage/shearimage.cc M +6 -0 plugins/extensions/shearimage/shearimage.h M +1 -1 plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp The files marked with a * at the end have a non valid license. Please read:= https://community.kde.org/Policies/Licensing_Policy and use the headers wh= ich are listed at that page. https://commits.kde.org/krita/3014d62c9817690ed21384450cb771c75bf60ce4 diff --git a/krita/krita.action b/krita/krita.action index 2a524c374f3..d247f8d0891 100644 --- a/krita/krita.action +++ b/krita/krita.action @@ -3303,6 +3303,102 @@ false + + symmetry-horizontal + Mirror All Layers Hori&zontally + + Mirror All Layers Horizontally + Mirror All Layers Horizontally + 1000 + 1 + + false + + + + symmetry-vertical + Mirror All Layers &Vertically + + Mirror All Layers Vertically + Mirror All Layers Vertically + 1000 + 1 + + false + + + + + &Rotate All Layers... + + Rotate All Layers + Rotate All Layers + 1000 + 1 + + false + + + + object-rotate-right + Rotate All &Layers 90=C2=B0 to the Right + + Rotate All Layers 90=C2=B0 to the Right + Rotate All Layers 90=C2=B0 to the Right + 1000 + 1 + + false + + + + object-rotate-left + Rotate All Layers &90=C2=B0 to the Left + + Rotate All Layers 90=C2=B0 to the Left + Rotate All Layers 90=C2=B0 to the Left + 1000 + 1 + + false + + + + + Rotate All Layers &180=C2=B0 + + Rotate All Layers 180=C2=B0 + Rotate All Layers 180=C2=B0 + 1000 + 1 + + false + + + + + Scale All &Layers to new Size... + + Scale All Layers to new Size + Scale All Layers to new Size + 100000 + 1 + + false + + + + + &Shear All Layers... + + Shear All Layers + Shear All Layers + 1000 + 1 + + false + + &Offset Layer... diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui index d050f120370..5e6da25a83f 100644 --- a/krita/krita4.xmlgui +++ b/krita/krita4.xmlgui @@ -236,6 +236,21 @@ xsi:schemaLocation=3D"http://www.kde.org/standards/kxm= lgui/1.0 http://www.kde.org + + Transform &All Layers + + + + + &Rotate + + + + + + + + S&plit diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index 06d757f161a..9696acc0ce7 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -110,12 +110,14 @@ set(kritaimage_LIB_SRCS commands_new/kis_switch_current_time_command.cpp commands_new/kis_change_projection_color_command.cpp commands_new/kis_activate_selection_mask_command.cpp + commands_new/kis_transaction_based_command.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_crop_processing_visitor.cpp processing/kis_crop_selections_processing_visitor.cpp processing/kis_transform_processing_visitor.cpp processing/kis_mirror_processing_visitor.cpp + processing/KisSelectionBasedProcessingHelper.cpp filter/kis_filter.cc filter/kis_filter_category_ids.cpp filter/kis_filter_configuration.cc diff --git a/libs/ui/kis_transaction_based_command.cpp b/libs/image/command= s_new/kis_transaction_based_command.cpp similarity index 100% rename from libs/ui/kis_transaction_based_command.cpp rename to libs/image/commands_new/kis_transaction_based_command.cpp diff --git a/libs/ui/kis_transaction_based_command.h b/libs/image/commands_= new/kis_transaction_based_command.h similarity index 92% rename from libs/ui/kis_transaction_based_command.h rename to libs/image/commands_new/kis_transaction_based_command.h index a47b1d40e0a..8c381bb4763 100644 --- a/libs/ui/kis_transaction_based_command.h +++ b/libs/image/commands_new/kis_transaction_based_command.h @@ -19,10 +19,10 @@ #ifndef KIS_TRANSACTION_BASED_COMMAND_H #define KIS_TRANSACTION_BASED_COMMAND_H = -#include +#include #include = -class KRITAUI_EXPORT KisTransactionBasedCommand : public KUndo2Command +class KRITAIMAGE_EXPORT KisTransactionBasedCommand : public KUndo2Command { public: KisTransactionBasedCommand(const KUndo2MagicString &text =3D KUndo2Mag= icString(), KUndo2Command *parent =3D 0); diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc index fbea2059cde..37b47fc1a4b 100644 --- a/libs/image/kis_image.cc +++ b/libs/image/kis_image.cc @@ -741,33 +741,64 @@ void KisImage::scaleImage(const QSize &size, qreal xr= es, qreal yres, KisFilterSt applicator.end(); } = -void KisImage::scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFi= lterStrategy *filterStrategy) +void KisImage::scaleNode(KisNodeSP node, const QPointF ¢er, qreal scal= eX, qreal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selecti= on) { KUndo2MagicString actionName(kundo2_i18n("Scale Layer")); KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; = + QPointF offset; + { + KisTransformWorker worker(0, + scaleX, scaleY, + 0, 0, 0, 0, + 0.0, + 0, 0, 0, 0); + QTransform transform =3D worker.transform(); + + offset =3D center - transform.map(center); + } + KisProcessingApplicator applicator(this, node, KisProcessingApplicator::RECURSIVE, emitSignals, actionName); = - KisProcessingVisitorSP visitor =3D + KisTransformProcessingVisitor *visitor =3D new KisTransformProcessingVisitor(scaleX, scaleY, 0, 0, QPointF(), 0, - 0, 0, + offset.x(), offset.y(), filterStrategy); = - applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT= ); + visitor->setSelection(selection); + + if (selection) { + applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); + } else { + applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCUR= RENT); + } + applicator.end(); } = void KisImage::rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, + double radians, bool resizeImage, - double radians) + KisSelectionSP selection) { + // we can either transform (and resize) the whole image or + // transform a selection, we cannot do both at the same time + KIS_SAFE_ASSERT_RECOVER(!(bool(selection) && resizeImage)) { + selection =3D 0; + } + + const QRect baseBounds =3D + resizeImage ? bounds() : + selection ? selection->selectedExactRect() : + rootNode->exactBounds(); + QPointF offset; QSize newSize; = @@ -780,12 +811,12 @@ void KisImage::rotateImpl(const KUndo2MagicString &ac= tionName, QTransform transform =3D worker.transform(); = if (resizeImage) { - QRect newRect =3D transform.mapRect(bounds()); + QRect newRect =3D transform.mapRect(baseBounds); newSize =3D newRect.size(); offset =3D -newRect.topLeft(); } else { - QPointF origin =3D QRectF(rootNode->exactBounds()).center(); + QPointF origin =3D QRectF(baseBounds).center(); = newSize =3D size(); offset =3D -(transform.map(origin) - origin); @@ -793,11 +824,12 @@ void KisImage::rotateImpl(const KUndo2MagicString &ac= tionName, } = bool sizeChanged =3D resizeImage && - (newSize.width() !=3D width() || newSize.height() !=3D height()); + (newSize.width() !=3D baseBounds.width() || + newSize.height() !=3D baseBounds.height()); = // These signals will be emitted after processing is done KisImageSignalVector emitSignals; - if (sizeChanged) emitSignals << ComplexSizeChangedSignal(bounds(), new= Size); + if (sizeChanged) emitSignals << ComplexSizeChangedSignal(baseBounds, n= ewSize); emitSignals << ModifiedSignal; = // These flags determine whether updates are transferred to the UI dur= ing processing @@ -813,14 +845,21 @@ void KisImage::rotateImpl(const KUndo2MagicString &ac= tionName, = KisFilterStrategy *filter =3D KisFilterStrategyRegistry::instance()->v= alue("Bicubic"); = - KisProcessingVisitorSP visitor =3D + KisTransformProcessingVisitor *visitor =3D new KisTransformProcessingVisitor(1.0, 1.0, 0.0, 0.0, QPointF(), radians, offset.x(), offset.y(), filter); + if (selection) { + visitor->setSelection(selection); + } = - applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT= ); + if (selection) { + applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); + } else { + applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCUR= RENT); + } = if (sizeChanged) { applicator.applyCommand(new KisImageResizeCommand(this, newSize)); @@ -831,15 +870,15 @@ void KisImage::rotateImpl(const KUndo2MagicString &ac= tionName, = void KisImage::rotateImage(double radians) { - rotateImpl(kundo2_i18n("Rotate Image"), root(), true, radians); + rotateImpl(kundo2_i18n("Rotate Image"), root(), radians, true, 0); } = -void KisImage::rotateNode(KisNodeSP node, double radians) +void KisImage::rotateNode(KisNodeSP node, double radians, KisSelectionSP s= election) { if (node->inherits("KisMask")) { - rotateImpl(kundo2_i18n("Rotate Mask"), node, false, radians); + rotateImpl(kundo2_i18n("Rotate Mask"), node, radians, false, selec= tion); } else { - rotateImpl(kundo2_i18n("Rotate Layer"), node, false, radians); + rotateImpl(kundo2_i18n("Rotate Layer"), node, radians, false, sele= ction); } } = @@ -847,8 +886,15 @@ void KisImage::shearImpl(const KUndo2MagicString &acti= onName, KisNodeSP rootNode, bool resizeImage, double angleX, double angleY, - const QPointF &origin) + KisSelectionSP selection) { + const QRect baseBounds =3D + resizeImage ? bounds() : + selection ? selection->selectedExactRect() : + rootNode->exactBounds(); + + const QPointF origin =3D QRectF(baseBounds).center(); + //angleX, angleY are in degrees const qreal pi =3D 3.1415926535897932385; const qreal deg2rad =3D pi / 180.0; @@ -866,15 +912,15 @@ void KisImage::shearImpl(const KUndo2MagicString &act= ionName, 0, 0, 0, 0, 0); = - QRect newRect =3D worker.transform().mapRect(bounds()); + QRect newRect =3D worker.transform().mapRect(baseBounds); newSize =3D newRect.size(); if (resizeImage) offset =3D -newRect.topLeft(); } = - if (newSize =3D=3D size()) return; + if (newSize =3D=3D baseBounds.size()) return; = KisImageSignalVector emitSignals; - if (resizeImage) emitSignals << ComplexSizeChangedSignal(bounds(), new= Size); + if (resizeImage) emitSignals << ComplexSizeChangedSignal(baseBounds, n= ewSize); emitSignals << ModifiedSignal; = KisProcessingApplicator::ProcessingFlags signalFlags =3D @@ -887,14 +933,22 @@ void KisImage::shearImpl(const KUndo2MagicString &act= ionName, = KisFilterStrategy *filter =3D KisFilterStrategyRegistry::instance()->v= alue("Bilinear"); = - KisProcessingVisitorSP visitor =3D + KisTransformProcessingVisitor *visitor =3D new KisTransformProcessingVisitor(1.0, 1.0, tanX, tanY, origin, 0, offset.x(), offset.y(), filter); = - applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCURRENT= ); + if (selection) { + visitor->setSelection(selection); + } + + if (selection) { + applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); + } else { + applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCUR= RENT); + } = if (resizeImage) { applicator.applyCommand(new KisImageResizeCommand(this, newSize)); @@ -903,23 +957,21 @@ void KisImage::shearImpl(const KUndo2MagicString &act= ionName, applicator.end(); } = -void KisImage::shearNode(KisNodeSP node, double angleX, double angleY) +void KisImage::shearNode(KisNodeSP node, double angleX, double angleY, Kis= SelectionSP selection) { - QPointF shearOrigin =3D QRectF(bounds()).center(); - if (node->inherits("KisMask")) { shearImpl(kundo2_i18n("Shear Mask"), node, false, - angleX, angleY, shearOrigin); + angleX, angleY, selection); } else { shearImpl(kundo2_i18n("Shear Layer"), node, false, - angleX, angleY, shearOrigin); + angleX, angleY, selection); } } = void KisImage::shear(double angleX, double angleY) { shearImpl(kundo2_i18n("Shear Image"), m_d->rootLayer, true, - angleX, angleY, QPointF()); + angleX, angleY, 0); } = void KisImage::convertImageColorSpace(const KoColorSpace *dstColorSpace, diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h index ba687623131..148ebd2da89 100644 --- a/libs/image/kis_image.h +++ b/libs/image/kis_image.h @@ -285,7 +285,7 @@ public: * a background, so you cannot expect the image having new size * right after ths call. */ - void scaleNode(KisNodeSP node, qreal scaleX, qreal scaleY, KisFilterSt= rategy *filterStrategy); + void scaleNode(KisNodeSP node, const QPointF ¢er, qreal scaleX, qr= eal scaleY, KisFilterStrategy *filterStrategy, KisSelectionSP selection); = /** * @brief start asynchronous operation on rotating the image @@ -312,7 +312,7 @@ public: * a background, so you cannot expect the operation being completed * right after the call */ - void rotateNode(KisNodeSP node, double radians); + void rotateNode(KisNodeSP node, double radians, KisSelectionSP selecti= on); = /** * @brief start asynchronous operation on shearing the image @@ -339,7 +339,7 @@ public: * a background, so you cannot expect the operation being completed * right after the call */ - void shearNode(KisNodeSP node, double angleX, double angleY); + void shearNode(KisNodeSP node, double angleX, double angleY, KisSelect= ionSP selection); = /** * Convert the image and all its layers to the dstColorSpace @@ -1066,11 +1066,10 @@ private: void emitSizeChanged(); = void resizeImageImpl(const QRect& newRect, bool cropLayers); - void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNod= e, - bool resizeImage, double radians); + void rotateImpl(const KUndo2MagicString &actionName, KisNodeSP rootNod= e, double radians, + bool resizeImage, KisSelectionSP selection); void shearImpl(const KUndo2MagicString &actionName, KisNodeSP rootNode, - bool resizeImage, double angleX, double angleY, - const QPointF &origin); + bool resizeImage, double angleX, double angleY, KisSele= ctionSP selection); = void safeRemoveTwoNodes(KisNodeSP node1, KisNodeSP node2); = diff --git a/libs/image/kis_processing_applicator.cpp b/libs/image/kis_proc= essing_applicator.cpp index f8d101b1980..08f493c3ed8 100644 --- a/libs/image/kis_processing_applicator.cpp +++ b/libs/image/kis_processing_applicator.cpp @@ -207,6 +207,12 @@ void KisProcessingApplicator::applyVisitor(KisProcessi= ngVisitorSP visitor, KisStrokeJobData::Sequentiality= sequentiality, KisStrokeJobData::Exclusivity e= xclusivity) { + KUndo2Command *initCommand =3D visitor->createInitCommand(); + if (initCommand) { + applyCommand(initCommand, + KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMA= L); + } + if(!m_flags.testFlag(RECURSIVE)) { applyCommand(new KisProcessingCommand(visitor, m_node), sequentiality, exclusivity); @@ -220,6 +226,12 @@ void KisProcessingApplicator::applyVisitorAllFrames(Ki= sProcessingVisitorSP visit KisStrokeJobData::Sequ= entiality sequentiality, KisStrokeJobData::Excl= usivity exclusivity) { + KUndo2Command *initCommand =3D visitor->createInitCommand(); + if (initCommand) { + applyCommand(initCommand, + KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMA= L); + } + KisLayerUtils::FrameJobs jobs; = // TODO: implement a nonrecursive case when !m_flags.testFlag(RECURSIV= E) diff --git a/libs/image/kis_processing_visitor.cpp b/libs/image/kis_process= ing_visitor.cpp index accd4ae9932..7aa9938ab86 100644 --- a/libs/image/kis_processing_visitor.cpp +++ b/libs/image/kis_processing_visitor.cpp @@ -56,3 +56,8 @@ KoUpdater* KisProcessingVisitor::ProgressHelper::updater(= ) const KisProcessingVisitor::~KisProcessingVisitor() { } + +KUndo2Command *KisProcessingVisitor::createInitCommand() +{ + return 0; +} diff --git a/libs/image/kis_processing_visitor.h b/libs/image/kis_processin= g_visitor.h index 6d3fe843698..4c5114897b5 100644 --- a/libs/image/kis_processing_visitor.h +++ b/libs/image/kis_processing_visitor.h @@ -39,6 +39,7 @@ class KisTransparencyMask; class KisSelectionMask; class KisGeneratorLayer; class KisColorizeMask; +class KUndo2Command; = /** * A visitor that processes a single layer; it does not recurse into the @@ -63,6 +64,13 @@ public: virtual void visit(KisColorizeMask *mask, KisUndoAdapter *undoAdapter)= =3D 0; virtual void visit(KisSelectionMask *mask, KisUndoAdapter *undoAdapter= ) =3D 0; = + /** + * Create a command that initializes the processing visitor before run= ning + * on all the layers. The command is executed sequentially, non-exclus= ively + * on the image by applicator. + */ + virtual KUndo2Command* createInitCommand(); + public: class KRITAIMAGE_EXPORT ProgressHelper { public: diff --git a/libs/image/processing/KisSelectionBasedProcessingHelper.cpp b/= libs/image/processing/KisSelectionBasedProcessingHelper.cpp new file mode 100644 index 00000000000..17fcb77bab7 --- /dev/null +++ b/libs/image/processing/KisSelectionBasedProcessingHelper.cpp @@ -0,0 +1,91 @@ +#include "KisSelectionBasedProcessingHelper.h" + +#include "kis_paint_device.h" +#include "kis_painter.h" +#include "kis_selection.h" +#include "kis_transaction_based_command.h" +#include "kis_transaction.h" +#include "kis_undo_adapter.h" + +KisSelectionBasedProcessingHelper::KisSelectionBasedProcessingHelper(KisSe= lectionSP selection, Functor func) + : m_selection(selection), + m_func(func) +{ +} + +void KisSelectionBasedProcessingHelper::setSelection(KisSelectionSP select= ion) +{ + m_selection =3D selection; +} + +KUndo2Command *KisSelectionBasedProcessingHelper::createInitCommand(Functo= r func) +{ + if (!m_selection) return 0; + + struct ProcessSelectionCommand : KisTransactionBasedCommand { + ProcessSelectionCommand(KisSelectionSP selection, + KisSelectionSP cutSelection, + std::function func) + : m_selection(selection), + m_cutSelection(cutSelection), + m_func(func) + { + } + + KUndo2Command* paint() { + m_cutSelection->pixelSelection()->makeCloneFromRough(m_selecti= on->pixelSelection(), m_selection->selectedRect()); + + KisTransaction t(m_selection->pixelSelection()); + m_func(m_selection->pixelSelection()); + + return t.endAndTake(); + } + + KisSelectionSP m_selection; + KisSelectionSP m_cutSelection; + Functor m_func; + }; + + m_cutSelection =3D new KisSelection(); + return new ProcessSelectionCommand(m_selection, m_cutSelection, func); +} + +KUndo2Command *KisSelectionBasedProcessingHelper::createInitCommand() +{ + return createInitCommand(m_func); +} + +void KisSelectionBasedProcessingHelper::transformPaintDevice(KisPaintDevic= eSP device, KisUndoAdapter *undoAdapter) +{ + transformPaintDevice(device, undoAdapter, m_func); +} + +void KisSelectionBasedProcessingHelper::transformPaintDevice(KisPaintDevic= eSP device, KisUndoAdapter *undoAdapter, Functor func) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(!!m_selection =3D=3D !!m_cutSelection); + + if (m_selection && m_cutSelection) { + // we have already processed the selection in the init command so = try to skip it + if (device !=3D static_cast(m_selection->pixelSel= ection().data())) { + KisTransaction transaction(device); + + const QRect cutBounds =3D m_cutSelection->selectedExactRect(); + const QRect pasteBounds =3D m_selection->selectedExactRect(); + + + KisPaintDeviceSP tempDev =3D new KisPaintDevice(device->colorS= pace()); + tempDev->makeCloneFromRough(device, cutBounds); + + func(tempDev); + + device->clearSelection(m_cutSelection); + KisPainter::copyAreaOptimized(pasteBounds.topLeft(), tempDev, = device, pasteBounds, m_selection); + transaction.commit(undoAdapter); + + } + } else { + KisTransaction transaction(device); + func(device); + transaction.commit(undoAdapter); + } +} diff --git a/libs/image/processing/KisSelectionBasedProcessingHelper.h b/li= bs/image/processing/KisSelectionBasedProcessingHelper.h new file mode 100644 index 00000000000..20bfa0b8455 --- /dev/null +++ b/libs/image/processing/KisSelectionBasedProcessingHelper.h @@ -0,0 +1,37 @@ +#ifndef KISSELECTIONBASEDPROCESSINGHELPER_H +#define KISSELECTIONBASEDPROCESSINGHELPER_H + +#include "kritaimage_export.h" +#include "kis_types.h" + +#include +#include + +class KisUndoAdapter; + + +class KisSelectionBasedProcessingHelper +{ +public: + using Functor =3D std::function; +public: + KisSelectionBasedProcessingHelper(KisSelectionSP selection, Functor fu= nc); + + void setSelection(KisSelectionSP selection); + + KUndo2Command *createInitCommand(); + KUndo2Command *createInitCommand(Functor func); + + + void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *und= oAdapter); + + void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *und= oAdapter, Functor func); + + +private: + KisSelectionSP m_selection; + KisSelectionSP m_cutSelection; + Functor m_func; +}; + +#endif // KISSELECTIONBASEDPROCESSINGHELPER_H diff --git a/libs/image/processing/kis_mirror_processing_visitor.cpp b/libs= /image/processing/kis_mirror_processing_visitor.cpp index 2dc840fba80..814cc2fe240 100644 --- a/libs/image/processing/kis_mirror_processing_visitor.cpp +++ b/libs/image/processing/kis_mirror_processing_visitor.cpp @@ -22,33 +22,50 @@ #include "kis_transaction.h" #include "kis_node.h" #include "kis_image.h" +#include "kis_painter.h" = #include "kis_transform_worker.h" #include "lazybrush/kis_colorize_mask.h" #include "processing/kis_transform_processing_visitor.h" = +#include "commands_new/kis_transaction_based_command.h" +#include + = KisMirrorProcessingVisitor::KisMirrorProcessingVisitor(const QRect &bounds= , Qt::Orientation orientation) - : m_bounds(bounds), m_orientation(orientation) + : m_bounds(bounds), + m_orientation(orientation), + m_selectionHelper(0, std::bind(&KisMirrorProcessingVisitor::mirrorDe= vice, this, std::placeholders::_1)) { + m_axis =3D m_orientation =3D=3D Qt::Horizontal ? + m_bounds.x() + 0.5 * m_bounds.width() : + m_bounds.y() + 0.5 * m_bounds.height(); } = -void KisMirrorProcessingVisitor::transformPaintDevice(KisPaintDeviceSP dev= ice, KisUndoAdapter *undoAdapter) +KisMirrorProcessingVisitor::KisMirrorProcessingVisitor(KisSelectionSP sele= ction, Qt::Orientation orientation) + : KisMirrorProcessingVisitor(selection->selectedExactRect(), orientati= on) { - KisTransaction transaction(device); + m_selectionHelper.setSelection(selection); +} = - qreal axis =3D m_orientation =3D=3D Qt::Horizontal ? - m_bounds.x() + 0.5 * m_bounds.width() : - m_bounds.y() + 0.5 * m_bounds.height(); +KUndo2Command *KisMirrorProcessingVisitor::createInitCommand() +{ + return m_selectionHelper.createInitCommand(); +} = - KisTransformWorker::mirror(device, axis, m_orientation); - transaction.commit(undoAdapter); +void KisMirrorProcessingVisitor::mirrorDevice(KisPaintDeviceSP device) +{ + KisTransformWorker::mirror(device, m_axis, m_orientation); +} + +void KisMirrorProcessingVisitor::transformPaintDevice(KisPaintDeviceSP dev= ice, KisUndoAdapter *undoAdapter) +{ + m_selectionHelper.transformPaintDevice(device, undoAdapter); } = void KisMirrorProcessingVisitor::visitNodeWithPaintDevice(KisNode *node, K= isUndoAdapter *undoAdapter) { transformPaintDevice(node->paintDevice(), undoAdapter); - } = void KisMirrorProcessingVisitor::visitExternalLayer(KisExternalLayer *laye= r, KisUndoAdapter *undoAdapter) diff --git a/libs/image/processing/kis_mirror_processing_visitor.h b/libs/i= mage/processing/kis_mirror_processing_visitor.h index e8f294070c0..e09f64c10e2 100644 --- a/libs/image/processing/kis_mirror_processing_visitor.h +++ b/libs/image/processing/kis_mirror_processing_visitor.h @@ -23,11 +23,14 @@ #include #include "kis_types.h" = +#include "KisSelectionBasedProcessingHelper.h" + = class KRITAIMAGE_EXPORT KisMirrorProcessingVisitor : public KisSimpleProce= ssingVisitor { public: KisMirrorProcessingVisitor(const QRect &bounds, Qt::Orientation orient= ation); + KisMirrorProcessingVisitor(KisSelectionSP selection, Qt::Orientation o= rientation); = private: void visitNodeWithPaintDevice(KisNode *node, KisUndoAdapter *undoAdapt= er) override; @@ -35,11 +38,18 @@ private: = void visitColorizeMask(KisColorizeMask *node, KisUndoAdapter *undoAdap= ter) override; = + KUndo2Command* createInitCommand() override; + + void mirrorDevice(KisPaintDeviceSP device); + private: void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *und= oAdapter); = QRect m_bounds; Qt::Orientation m_orientation; + qreal m_axis =3D 0.0; + + KisSelectionBasedProcessingHelper m_selectionHelper; }; = #endif /* __KIS_MIRROR_PROCESSING_VISITOR_H */ diff --git a/libs/image/processing/kis_transform_processing_visitor.cpp b/l= ibs/image/processing/kis_transform_processing_visitor.cpp index d62099c536f..0e6f61a9cdb 100644 --- a/libs/image/processing/kis_transform_processing_visitor.cpp +++ b/libs/image/processing/kis_transform_processing_visitor.cpp @@ -62,9 +62,24 @@ KisTransformProcessingVisitor(qreal xscale, qreal ysca= le, , m_filter(filter) , m_angle(angle) , m_shapesCorrection(shapesCorrection) + , m_selectionHelper(0, KisSelectionBasedProcessingHelper::Functor()) { } = +void KisTransformProcessingVisitor::setSelection(KisSelectionSP selection) +{ + m_selectionHelper.setSelection(selection); +} + +KUndo2Command *KisTransformProcessingVisitor::createInitCommand() +{ + return m_selectionHelper.createInitCommand( + std::bind(&KisTransformProcessingVisitor::transformOneDevice, + this, + std::placeholders::_1, + (KoUpdater*)0)); +} + void KisTransformProcessingVisitor::visit(KisNode *node, KisUndoAdapter *u= ndoAdapter) { Q_UNUSED(node); @@ -186,6 +201,16 @@ void KisTransformProcessingVisitor::transformClones(Ki= sLayer *layer, KisUndoAdap = void KisTransformProcessingVisitor::transformPaintDevice(KisPaintDeviceSP = device, KisUndoAdapter *adapter, const ProgressHelper &helper) { + m_selectionHelper.transformPaintDevice(device, + adapter, + std::bind(&KisTransformProcessi= ngVisitor::transformOneDevice, + this, + std::placeholders::_1, + helper.updater())); + + + return; + KisTransaction transaction(kundo2_i18n("Transform Layer"), device); = KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary, @@ -196,12 +221,18 @@ void KisTransformProcessingVisitor::transformPaintDev= ice(KisPaintDeviceSP device transaction.commit(adapter); } = -void KisTransformProcessingVisitor::transformSelection(KisSelectionSP sele= ction, KisUndoAdapter *adapter, const ProgressHelper &helper) +void KisTransformProcessingVisitor::transformOneDevice(KisPaintDeviceSP de= vice, + KoUpdater *updater) { - if(selection->hasPixelSelection()) { - transformPaintDevice(selection->pixelSelection(), adapter, helper); - } + KisTransformWorker tw(device, m_sx, m_sy, m_shearx, m_sheary, + m_shearOrigin.x(), m_shearOrigin.y(), + m_angle, m_tx, m_ty, updater, + m_filter); + tw.run(); +} = +void KisTransformProcessingVisitor::transformSelection(KisSelectionSP sele= ction, KisUndoAdapter *adapter, const ProgressHelper &helper) +{ if (selection->hasShapeSelection()) { KisTransformWorker tw(selection->projection(), m_sx, m_sy, m_shear= x, m_sheary, m_shearOrigin.x(), m_shearOrigin.y(), @@ -213,8 +244,9 @@ void KisTransformProcessingVisitor::transformSelection(= KisSelectionSP selection, if (command) { adapter->addCommand(command); } + } else { + transformPaintDevice(selection->pixelSelection(), adapter, helper); } = selection->updateProjection(); } - diff --git a/libs/image/processing/kis_transform_processing_visitor.h b/lib= s/image/processing/kis_transform_processing_visitor.h index e72fa64bbdc..d8b8a66e6a3 100644 --- a/libs/image/processing/kis_transform_processing_visitor.h +++ b/libs/image/processing/kis_transform_processing_visitor.h @@ -20,7 +20,7 @@ #define __KIS_TRANSFORM_PROCESSING_VISITOR_H = #include "kis_processing_visitor.h" - +#include "KisSelectionBasedProcessingHelper.h" = #include = @@ -39,6 +39,10 @@ public: KisFilterStrategy *filter, const QTransform &shapesCorrection =3D Q= Transform()); = + void setSelection(KisSelectionSP selection); + KUndo2Command *createInitCommand(); + + void visit(KisNode *node, KisUndoAdapter *undoAdapter) override; void visit(KisPaintLayer *layer, KisUndoAdapter *undoAdapter) override; void visit(KisGroupLayer *layer, KisUndoAdapter *undoAdapter) override; @@ -57,6 +61,8 @@ private: void transformPaintDevice(KisPaintDeviceSP device, KisUndoAdapter *ada= pter, const ProgressHelper &helper); void transformSelection(KisSelectionSP selection, KisUndoAdapter *adap= ter, const ProgressHelper &helper); = + void transformOneDevice(KisPaintDeviceSP device, KoUpdater *updater); + private: qreal m_sx, m_sy; qint32 m_tx, m_ty; @@ -65,6 +71,7 @@ private: KisFilterStrategy *m_filter; qreal m_angle; QTransform m_shapesCorrection; + KisSelectionBasedProcessingHelper m_selectionHelper; }; = #endif /* __KIS_TRANSFORM_PROCESSING_VISITOR_H */ diff --git a/libs/libkis/Node.cpp b/libs/libkis/Node.cpp index 3341db586bb..521dc6e5082 100644 --- a/libs/libkis/Node.cpp +++ b/libs/libkis/Node.cpp @@ -50,6 +50,7 @@ = #include #include +#include "kis_selection.h" = #include "Krita.h" #include "Node.h" @@ -546,7 +547,7 @@ Node* Node::mergeDown() return new Node(d->image, d->node->prevSibling()); } = -void Node::scaleNode(int width, int height, QString strategy) +void Node::scaleNode(const QPointF &origin, int width, int height, QString= strategy) { if (!d->node) return; if (!qobject_cast(d->node.data())) return; @@ -555,7 +556,13 @@ void Node::scaleNode(int width, int height, QString st= rategy) KisFilterStrategy *actualStrategy =3D KisFilterStrategyRegistry::insta= nce()->get(strategy); if (!actualStrategy) actualStrategy =3D KisFilterStrategyRegistry::ins= tance()->get("Bicubic"); = - d->image->scaleNode(d->node, width, height, actualStrategy); + const QRect bounds(d->node->exactBounds()); + + d->image->scaleNode(d->node, + origin, + qreal(width) / bounds.width(), + qreal(height) / bounds.height(), + actualStrategy, 0); } = void Node::rotateNode(double radians) @@ -564,7 +571,7 @@ void Node::rotateNode(double radians) if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; = - d->image->rotateNode(d->node, radians); + d->image->rotateNode(d->node, radians, 0); } = void Node::cropNode(int x, int y, int w, int h) @@ -583,7 +590,7 @@ void Node::shearNode(double angleX, double angleY) if (!qobject_cast(d->node.data())) return; if (!d->node->parent()) return; = - d->image->shearNode(d->node, angleX, angleY); + d->image->shearNode(d->node, angleX, angleY, 0); } = QImage Node::thumbnail(int w, int h) diff --git a/libs/libkis/Node.h b/libs/libkis/Node.h index 790ee974c6e..11019b73d49 100644 --- a/libs/libkis/Node.h +++ b/libs/libkis/Node.h @@ -489,7 +489,7 @@ public Q_SLOTS: *
  • Mitchell
  • * */ - void scaleNode(int width, int height, QString strategy); + void scaleNode(const QPointF &origin, int width, int height, QString s= trategy); = /** * @brief rotateNode rotate this layer by the given radians. diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index bb15a54a192..74973e3ba86 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -294,7 +294,6 @@ set(kritaui_LIB_SRCS actions/KisPasteActionFactory.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp - kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/a= ctions/kis_selection_action_factories.cpp index 5a0b4bb13b3..5952680035d 100644 --- a/libs/ui/actions/kis_selection_action_factories.cpp +++ b/libs/ui/actions/kis_selection_action_factories.cpp @@ -54,7 +54,7 @@ #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_selection_manager.h" -#include "kis_transaction_based_command.h" +#include "commands_new/kis_transaction_based_command.h" #include "kis_selection_filters.h" #include "kis_shape_selection.h" #include "kis_shape_layer.h" diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp index bf1eab182d1..45acb4cf796 100644 --- a/libs/ui/kis_node_manager.cpp +++ b/libs/ui/kis_node_manager.cpp @@ -251,12 +251,20 @@ void KisNodeManager::setup(KActionCollection * action= Collection, KisActionManage m_d->layerManager.setup(actionManager); m_d->maskManager.setup(actionCollection, actionManager); = - KisAction * action =3D actionManager->createAction("mirrorNodeX"); + KisAction * action =3D 0; + + action =3D actionManager->createAction("mirrorNodeX"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeX())); = action =3D actionManager->createAction("mirrorNodeY"); connect(action, SIGNAL(triggered()), this, SLOT(mirrorNodeY())); = + action =3D actionManager->createAction("mirrorAllNodesX"); + connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesX())); + + action =3D actionManager->createAction("mirrorAllNodesY"); + connect(action, SIGNAL(triggered()), this, SLOT(mirrorAllNodesY())); + action =3D actionManager->createAction("activateNextLayer"); connect(action, SIGNAL(triggered()), this, SLOT(activateNextNode())); = @@ -904,7 +912,7 @@ void KisNodeManager::mirrorNodeX() } else if (node->inherits("KisMask")) { commandName =3D kundo2_i18n("Mirror Mask X"); } - mirrorNode(node, commandName, Qt::Horizontal); + mirrorNode(node, commandName, Qt::Horizontal, m_d->view->selection()); } = void KisNodeManager::mirrorNodeY() @@ -917,7 +925,21 @@ void KisNodeManager::mirrorNodeY() } else if (node->inherits("KisMask")) { commandName =3D kundo2_i18n("Mirror Mask Y"); } - mirrorNode(node, commandName, Qt::Vertical); + mirrorNode(node, commandName, Qt::Vertical, m_d->view->selection()); +} + +void KisNodeManager::mirrorAllNodesX() +{ + KisNodeSP node =3D m_d->view->image()->root(); + mirrorNode(node, kundo2_i18n("Mirror All Layers X"), + Qt::Vertical, m_d->view->selection()); +} + +void KisNodeManager::mirrorAllNodesY() +{ + KisNodeSP node =3D m_d->view->image()->root(); + mirrorNode(node, kundo2_i18n("Mirror All Layers Y"), + Qt::Vertical, m_d->view->selection()); } = void KisNodeManager::activateNextNode() @@ -979,56 +1001,10 @@ void KisNodeManager::switchToPreviouslyActiveNode() } } = -void KisNodeManager::rotate(double radians) -{ - if(!m_d->view->image()) return; - - KisNodeSP node =3D activeNode(); - if (!node) return; - - if (!m_d->view->blockUntilOperationsFinished(m_d->view->image())) retu= rn; - - m_d->view->image()->rotateNode(node, radians); -} - -void KisNodeManager::rotate180() -{ - rotate(M_PI); -} - -void KisNodeManager::rotateLeft90() -{ - rotate(-M_PI / 2); -} - -void KisNodeManager::rotateRight90() -{ - rotate(M_PI / 2); -} - -void KisNodeManager::shear(double angleX, double angleY) -{ - if (!m_d->view->image()) return; - - KisNodeSP node =3D activeNode(); - if (!node) return; - - if(!m_d->view->blockUntilOperationsFinished(m_d->view->image())) retur= n; - - m_d->view->image()->shearNode(node, angleX, angleY); -} - -void KisNodeManager::scale(double sx, double sy, KisFilterStrategy *filter= Strategy) -{ - KisNodeSP node =3D activeNode(); - KIS_ASSERT_RECOVER_RETURN(node); - - m_d->view->image()->scaleNode(node, sx, sy, filterStrategy); - - nodesUpdated(); -} - -void KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& a= ctionName, Qt::Orientation orientation) +void KisNodeManager::mirrorNode(KisNodeSP node, + const KUndo2MagicString& actionName, + Qt::Orientation orientation, + KisSelectionSP selection) { KisImageSignalVector emitSignals; emitSignals << ModifiedSignal; @@ -1037,10 +1013,20 @@ void KisNodeManager::mirrorNode(KisNodeSP node, con= st KUndo2MagicString& actionN KisProcessingApplicator::RECURSIVE, emitSignals, actionName); = - KisProcessingVisitorSP visitor =3D - new KisMirrorProcessingVisitor(m_d->view->image()->bounds(), orien= tation); + KisProcessingVisitorSP visitor; + + if (selection) { + visitor =3D new KisMirrorProcessingVisitor(selection, orientation); + } else { + visitor =3D new KisMirrorProcessingVisitor(m_d->view->image()->bou= nds(), orientation); + } + + if (!selection) { + applicator.applyVisitorAllFrames(visitor, KisStrokeJobData::CONCUR= RENT); + } else { + applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); + } = - applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); applicator.end(); = nodesUpdated(); diff --git a/libs/ui/kis_node_manager.h b/libs/ui/kis_node_manager.h index dabe293cb2b..29c80823d02 100644 --- a/libs/ui/kis_node_manager.h +++ b/libs/ui/kis_node_manager.h @@ -180,7 +180,13 @@ public Q_SLOTS: void removeNode(); void mirrorNodeX(); void mirrorNodeY(); - void mirrorNode(KisNodeSP node, const KUndo2MagicString& commandName, = Qt::Orientation orientation); + void mirrorAllNodesX(); + void mirrorAllNodesY(); + + + void mirrorNode(KisNodeSP node, const KUndo2MagicString& commandName, = Qt::Orientation orientation, KisSelectionSP selection); + + void activateNextNode(); void activatePreviousNode(); void switchToPreviouslyActiveNode(); @@ -195,11 +201,6 @@ public Q_SLOTS: */ void lowerNode(); = - void rotate(double radians); - void rotate180(); - void rotateLeft90(); - void rotateRight90(); - void saveNodeAsImage(); void saveVectorLayerAsImage(); = @@ -235,12 +236,6 @@ public Q_SLOTS: void selectUnlockedNodes(); = public: - - - void shear(double angleX, double angleY); - - void scale(double sx, double sy, KisFilterStrategy *filterStrategy); - void removeSingleNode(KisNodeSP node); KisLayerSP createPaintLayer(); = diff --git a/libs/ui/operations/kis_filter_selection_operation.cpp b/libs/u= i/operations/kis_filter_selection_operation.cpp index d0e3226bb9c..ed02df14574 100644 --- a/libs/ui/operations/kis_filter_selection_operation.cpp +++ b/libs/ui/operations/kis_filter_selection_operation.cpp @@ -19,7 +19,7 @@ = = #include "kis_filter_selection_operation.h" -#include +#include #include #include #include diff --git a/libs/ui/tests/kis_node_manager_test.cpp b/libs/ui/tests/kis_no= de_manager_test.cpp index c8c293d0836..9486dfbf0ea 100644 --- a/libs/ui/tests/kis_node_manager_test.cpp +++ b/libs/ui/tests/kis_node_manager_test.cpp @@ -68,80 +68,6 @@ public: KisNodeManager *nodeManager; }; = -void testRotateNode(bool useShapeLayer, const QString &name) -{ - NodeManagerTester t; - if(useShapeLayer) { - t.activateShapeLayer(); - } - - t.nodeManager->rotate(M_PI / 6.0); - QTest::qWait(1000); - t.image->waitForDone(); - QVERIFY(t.checkLayersFuzzy(name)); - - t.checkUndoWait(); - t.startConcurrentTask(); - - t.nodeManager->rotate(M_PI / 6.0); - QTest::qWait(1000); - t.image->waitForDone(); - - if (!useShapeLayer) { - QEXPECT_FAIL("", "The user may run Rotate Layer concurrently. It w= ill cause wrong image/selection size fetched for the crop. There is some ba= rrier needed. At least it doesn't crash.", Continue); - } - QVERIFY(t.checkLayersFuzzy(name)); -} - -void testShearNode(bool useShapeLayer, const QString &name) -{ - NodeManagerTester t; - if(useShapeLayer) { - t.activateShapeLayer(); - } - - t.nodeManager->shear(30, 0); - QTest::qWait(1000); - t.image->waitForDone(); - QVERIFY(t.checkLayersFuzzy(name)); - - t.checkUndoWait(); - t.startConcurrentTask(); - - t.nodeManager->shear(30, 0); - QTest::qWait(1000); - t.image->waitForDone(); - - QEXPECT_FAIL("", "The user may run Shear Layer concurrently. It will c= ause wrong image/selection size fetched for the crop. There is some barrier= needed. At least it doesn't crash.", Continue); - QVERIFY(t.checkLayersFuzzy(name)); -} - -void testScaleNode(bool useShapeLayer, const QString &name) -{ - KisFilterStrategy *strategy =3D new KisBicubicFilterStrategy(); - - NodeManagerTester t; - if(useShapeLayer) { - t.activateShapeLayer(); - } - - t.nodeManager->scale(0.5, 0.5, strategy); - QTest::qWait(1000); - t.image->waitForDone(); - QVERIFY(t.checkLayersFuzzy(name)); - - t.checkUndoWait(); - t.startConcurrentTask(); - - t.nodeManager->scale(0.5, 0.5, strategy); - QTest::qWait(1000); - t.image->waitForDone(); - - QVERIFY(t.checkLayersFuzzy(name)); - - delete strategy; -} - void testMirrorNode(bool useShapeLayer, const QString &name, bool mirrorX) { NodeManagerTester t; @@ -173,21 +99,6 @@ void testMirrorNode(bool useShapeLayer, const QString &= name, bool mirrorX) QVERIFY(t.checkLayersFuzzy(name)); } = -void KisNodeManagerTest::testRotatePaintNode() -{ - testRotateNode(false, "paint_rotated_30"); -} - -void KisNodeManagerTest::testShearPaintNode() -{ - testShearNode(false, "paint_shear_30"); -} - -void KisNodeManagerTest::testScalePaintNode() -{ - testScaleNode(false, "paint_scale_0.5"); -} - void KisNodeManagerTest::testMirrorXPaintNode() { testMirrorNode(false, "paint_mirrorX", true); @@ -198,21 +109,6 @@ void KisNodeManagerTest::testMirrorYPaintNode() testMirrorNode(false, "paint_mirrorY", false); } = -void KisNodeManagerTest::testRotateShapeNode() -{ - testRotateNode(true, "shape_rotated_30"); -} - -void KisNodeManagerTest::testShearShapeNode() -{ - testShearNode(true, "shape_shear_30"); -} - -void KisNodeManagerTest::testScaleShapeNode() -{ - testScaleNode(true, "shape_scale_0.5"); -} - void KisNodeManagerTest::testMirrorShapeNode() { testMirrorNode(true, "shape_mirrorX", true); diff --git a/libs/ui/tests/kis_node_manager_test.h b/libs/ui/tests/kis_node= _manager_test.h index 0401b95a1b4..458d512213a 100644 --- a/libs/ui/tests/kis_node_manager_test.h +++ b/libs/ui/tests/kis_node_manager_test.h @@ -25,15 +25,9 @@ class KisNodeManagerTest : public QObject { Q_OBJECT private Q_SLOTS: - void testRotatePaintNode(); - void testShearPaintNode(); - void testScalePaintNode(); void testMirrorXPaintNode(); void testMirrorYPaintNode(); = - void testRotateShapeNode(); - void testShearShapeNode(); - void testScaleShapeNode(); void testMirrorShapeNode(); = void testConvertCloneToPaintLayer(); diff --git a/libs/ui/tool/kis_selection_tool_helper.cpp b/libs/ui/tool/kis_= selection_tool_helper.cpp index 198fae9ac7b..fa3684dd249 100644 --- a/libs/ui/tool/kis_selection_tool_helper.cpp +++ b/libs/ui/tool/kis_selection_tool_helper.cpp @@ -36,7 +36,7 @@ = #include #include "kis_processing_applicator.h" -#include "kis_transaction_based_command.h" +#include "commands_new/kis_transaction_based_command.h" #include "kis_gui_context_command.h" #include "kis_command_utils.h" #include "commands/kis_deselect_global_selection_command.h" diff --git a/plugins/dockers/gamutmask/gamutmask_dock.cpp b/plugins/dockers= /gamutmask/gamutmask_dock.cpp index 781b0244f97..5343eefe7ad 100644 --- a/plugins/dockers/gamutmask/gamutmask_dock.cpp +++ b/plugins/dockers/gamutmask/gamutmask_dock.cpp @@ -352,7 +352,7 @@ KoGamutMask *GamutMaskDock::createMaskResource(KoGamutM= ask* sourceMask, QString newMask =3D new KoGamutMask(); = QString defaultPreviewPath =3D KoResourcePaths::findResource("ko_g= amutmasks", "empty_mask_preview.png"); - KIS_SAFE_ASSERT_RECOVER(!(defaultPreviewPath.isEmpty() || defaultP= reviewPath.isNull() || !QFile::exists(defaultPreviewPath))); + KIS_SAFE_ASSERT_RECOVER_NOOP(!(defaultPreviewPath.isEmpty() || def= aultPreviewPath.isNull() || !QFile::exists(defaultPreviewPath))); = newMask->setImage(QImage(defaultPreviewPath, "PNG")); } diff --git a/plugins/extensions/imagesize/imagesize.cc b/plugins/extensions= /imagesize/imagesize.cc index 7adb79aaa0e..236afc02f17 100644 --- a/plugins/extensions/imagesize/imagesize.cc +++ b/plugins/extensions/imagesize/imagesize.cc @@ -57,6 +57,9 @@ ImageSize::ImageSize(QObject *parent, const QVariantList = &) action =3D createAction("layersize"); connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSize())); = + action =3D createAction("scaleAllLayers"); + connect(action, SIGNAL(triggered()), this, SLOT(slotScaleAllLayers())); + action =3D createAction("selectionscale"); connect(action, SIGNAL(triggered()), this, SLOT(slotSelectionScale())); } @@ -70,6 +73,8 @@ void ImageSize::slotImageSize() KisImageSP image =3D viewManager()->image().toStrongRef(); if (!image) return; = + if(!viewManager()->blockUntilOperationsFinished(image)) return; + DlgImageSize * dlgImageSize =3D new DlgImageSize(viewManager()->mainWi= ndow(), image->width(), image->height(), image->yRes()); Q_CHECK_PTR(dlgImageSize); dlgImageSize->setObjectName("ImageSize"); @@ -88,9 +93,10 @@ void ImageSize::slotImageSize() void ImageSize::slotCanvasSize() { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = + if(!viewManager()->blockUntilOperationsFinished(image)) return; + DlgCanvasSize * dlgCanvasSize =3D new DlgCanvasSize(viewManager()->mai= nWindow(), image->width(), image->height(), image->yRes()); Q_CHECK_PTR(dlgCanvasSize); = @@ -105,17 +111,25 @@ void ImageSize::slotCanvasSize() delete dlgCanvasSize; } = -void ImageSize::slotLayerSize() +void ImageSize::scaleLayerImpl(KisNodeSP rootNode) { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = - KisPaintDeviceSP dev =3D viewManager()->activeLayer()->projection(); - Q_ASSERT(dev); - QRect rc =3D dev->exactBounds(); + if(!viewManager()->blockUntilOperationsFinished(image)) return; = - DlgLayerSize * dlgLayerSize =3D new DlgLayerSize(viewManager()->mainWi= ndow(), "LayerSize", rc.width(), rc.height(), image->yRes()); + QRect bounds; + KisSelectionSP selection =3D viewManager()->selection(); + + if (selection) { + bounds =3D selection->selectedExactRect(); + } else { + KisPaintDeviceSP dev =3D rootNode->projection(); + KIS_SAFE_ASSERT_RECOVER_RETURN(dev); + bounds =3D dev->exactBounds(); + } + + DlgLayerSize * dlgLayerSize =3D new DlgLayerSize(viewManager()->mainWi= ndow(), "LayerSize", bounds.width(), bounds.height(), image->yRes()); Q_CHECK_PTR(dlgLayerSize); dlgLayerSize->setCaption(i18n("Resize Layer")); = @@ -123,19 +137,36 @@ void ImageSize::slotLayerSize() qint32 w =3D dlgLayerSize->width(); qint32 h =3D dlgLayerSize->height(); = - viewManager()->nodeManager()->scale((double)w / ((double)(rc.width= ())), - (double)h / ((double)(rc.height())), - dlgLayerSize->filterType()); + viewManager()->image()->scaleNode(rootNode, + QRectF(bounds).center(), + qreal(w) / bounds.width(), + qreal(h) / bounds.height(), + dlgLayerSize->filterType(), + selection); } delete dlgLayerSize; } = +void ImageSize::slotLayerSize() +{ + scaleLayerImpl(viewManager()->activeNode()); +} + +void ImageSize::slotScaleAllLayers() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + scaleLayerImpl(image->root()); +} + void ImageSize::slotSelectionScale() { KisImageSP image =3D viewManager()->image(); - if (!image) { - return; - } + if (!image) return; + + if(!viewManager()->blockUntilOperationsFinished(image)) return; + KisLayerSP layer =3D viewManager()->activeLayer(); = KIS_ASSERT_RECOVER_RETURN(image && layer); @@ -156,9 +187,10 @@ void ImageSize::slotSelectionScale() qint32 h =3D dlgSize->height(); = image->scaleNode(selectionMask, + QRectF(rc).center(), qreal(w) / rc.width(), qreal(h) / rc.height(), - dlgSize->filterType()); + dlgSize->filterType(), 0); } delete dlgSize; } diff --git a/plugins/extensions/imagesize/imagesize.h b/plugins/extensions/= imagesize/imagesize.h index 892fc333eeb..bcf1093031d 100644 --- a/plugins/extensions/imagesize/imagesize.h +++ b/plugins/extensions/imagesize/imagesize.h @@ -23,6 +23,7 @@ #include = #include +#include "kis_types.h" = class ImageSize : public KisActionPlugin { @@ -31,12 +32,17 @@ public: ImageSize(QObject *parent, const QVariantList &); ~ImageSize() override; = +private: + void scaleLayerImpl(KisNodeSP rootNode); + private Q_SLOTS: = void slotImageSize(); void slotCanvasSize(); void slotLayerSize(); void slotSelectionScale(); + + void slotScaleAllLayers(); }; = #endif // IMAGESIZE_H diff --git a/plugins/extensions/pykrita/sip/krita/Node.sip b/plugins/extens= ions/pykrita/sip/krita/Node.sip index fdd535229e5..b52cb24d5c9 100644 --- a/plugins/extensions/pykrita/sip/krita/Node.sip +++ b/plugins/extensions/pykrita/sip/krita/Node.sip @@ -55,7 +55,7 @@ public Q_SLOTS: Node *duplicate() /Factory/; void save(const QString &filename, double xRes, double yRes); Node *mergeDown() /Factory/; - void scaleNode(int width, int height, QString strategy); + void scaleNode(const QPointF &origin, int width, int height, QString s= trategy); void rotateNode(double radians); void cropNode(int x, int y, int w, int h); void shearNode(double angleX, double angleY); diff --git a/plugins/extensions/rotateimage/rotateimage.cc b/plugins/extens= ions/rotateimage/rotateimage.cc index 97dd9bdeca7..795060f92fd 100644 --- a/plugins/extensions/rotateimage/rotateimage.cc +++ b/plugins/extensions/rotateimage/rotateimage.cc @@ -35,6 +35,7 @@ #include #include #include +#include = #include "dlg_rotateimage.h" = @@ -66,13 +67,25 @@ RotateImage::RotateImage(QObject *parent, const QVarian= tList &) connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayer())); = action =3D createAction("rotateLayer180"); - connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLO= T(rotate180())); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayer180())); = action =3D createAction("rotateLayerCW90"); - connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLO= T(rotateRight90())); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayerCW90())= ); = action =3D createAction("rotateLayerCCW90"); - connect(action, SIGNAL(triggered()), viewManager()->nodeManager(), SLO= T(rotateLeft90())); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateLayerCCW90()= )); + + action =3D createAction("rotateAllLayers"); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayers())= ); + + action =3D createAction("rotateAllLayersCW90"); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayersCW9= 0())); + + action =3D createAction("rotateAllLayersCCW90"); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayersCCW= 90())); + + action =3D createAction("rotateAllLayers180"); + connect(action, SIGNAL(triggered()), this, SLOT(slotRotateAllLayers180= ())); } = RotateImage::~RotateImage() @@ -82,9 +95,10 @@ RotateImage::~RotateImage() void RotateImage::slotRotateImage() { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = + if (!viewManager()->blockUntilOperationsFinished(image)) return; + DlgRotateImage * dlgRotateImage =3D new DlgRotateImage(viewManager()->= mainWindow(), "RotateImage"); Q_CHECK_PTR(dlgRotateImage); = @@ -116,22 +130,28 @@ void RotateImage::slotMirrorImageVertical() { KisImageWSP image =3D viewManager()->image(); if (!image) return; - viewManager()->nodeManager()->mirrorNode(image->rootLayer(), kundo2_i1= 8n("Mirror Image Vertically"), Qt::Vertical); + viewManager()->nodeManager()->mirrorNode(image->rootLayer(), + kundo2_i18n("Mirror Image Ver= tically"), + Qt::Vertical, 0); } = void RotateImage::slotMirrorImageHorizontal() { KisImageWSP image =3D viewManager()->image(); if (!image) return; - viewManager()->nodeManager()->mirrorNode(image->rootLayer(), kundo2_i1= 8n("Mirror Image Horizontally"), Qt::Horizontal); + viewManager()->nodeManager()->mirrorNode(image->rootLayer(), + kundo2_i18n("Mirror Image Hor= izontally"), + Qt::Horizontal, 0); } = -void RotateImage::slotRotateLayer() + +void RotateImage::rotateLayerCustomImpl(KisNodeSP rootNode) { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = + if (!viewManager()->blockUntilOperationsFinished(image)) return; + DlgRotateImage * dlgRotateImage =3D new DlgRotateImage(viewManager()->= mainWindow(), "RotateLayer"); Q_CHECK_PTR(dlgRotateImage); = @@ -139,10 +159,71 @@ void RotateImage::slotRotateLayer() = if (dlgRotateImage->exec() =3D=3D QDialog::Accepted) { double angle =3D dlgRotateImage->angle() * M_PI / 180; - viewManager()->nodeManager()->rotate(angle); - + image->rotateNode(rootNode, angle, viewManager()->selection()); } delete dlgRotateImage; } = +void RotateImage::rotateLayerImpl(KisNodeSP rootNode, qreal radians) +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + if (!viewManager()->blockUntilOperationsFinished(image)) return; + + image->rotateNode(rootNode, radians, viewManager()->selection()); +} + +void RotateImage::slotRotateLayer() +{ + rotateLayerCustomImpl(viewManager()->activeLayer()); +} + +void RotateImage::slotRotateAllLayers() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + rotateLayerCustomImpl(image->root()); +} + +void RotateImage::slotRotateLayerCW90() +{ + rotateLayerImpl(viewManager()->activeLayer(), M_PI / 2); +} + +void RotateImage::slotRotateLayerCCW90() +{ + rotateLayerImpl(viewManager()->activeLayer(), -M_PI / 2); +} + +void RotateImage::slotRotateLayer180() +{ + rotateLayerImpl(viewManager()->activeLayer(), M_PI); +} + +void RotateImage::slotRotateAllLayersCW90() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + rotateLayerImpl(image->root(), M_PI / 2); +} + +void RotateImage::slotRotateAllLayersCCW90() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + rotateLayerImpl(image->root(), -M_PI / 2); +} + +void RotateImage::slotRotateAllLayers180() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + rotateLayerImpl(image->root(), M_PI); +} + #include "rotateimage.moc" diff --git a/plugins/extensions/rotateimage/rotateimage.h b/plugins/extensi= ons/rotateimage/rotateimage.h index b0e88dcb23d..22e4d5e8019 100644 --- a/plugins/extensions/rotateimage/rotateimage.h +++ b/plugins/extensions/rotateimage/rotateimage.h @@ -23,6 +23,7 @@ #include = #include +#include "kis_types.h" = class RotateImage : public KisActionPlugin { @@ -31,6 +32,10 @@ public: RotateImage(QObject *parent, const QVariantList &); ~RotateImage() override; = +private: + void rotateLayerCustomImpl(KisNodeSP rootNode); + void rotateLayerImpl(KisNodeSP rootNode, qreal radians); + private Q_SLOTS: = void slotRotateImage(); @@ -40,6 +45,13 @@ private Q_SLOTS: void slotMirrorImageVertical(); void slotMirrorImageHorizontal(); void slotRotateLayer(); + void slotRotateLayerCW90(); + void slotRotateLayerCCW90(); + void slotRotateLayer180(); + void slotRotateAllLayers(); + void slotRotateAllLayersCW90(); + void slotRotateAllLayersCCW90(); + void slotRotateAllLayers180(); }; = #endif // ROTATEIMAGE_H diff --git a/plugins/extensions/shearimage/shearimage.cc b/plugins/extensio= ns/shearimage/shearimage.cc index ff009807d27..a89bca0d11e 100644 --- a/plugins/extensions/shearimage/shearimage.cc +++ b/plugins/extensions/shearimage/shearimage.cc @@ -28,6 +28,7 @@ #include #include #include +#include "kis_selection.h" = #include "dlg_shearimage.h" = @@ -41,6 +42,9 @@ ShearImage::ShearImage(QObject *parent, const QVariantLis= t &) = action =3D createAction("shearlayer"); connect(action, SIGNAL(triggered()), this, SLOT(slotShearLayer())); + + action =3D createAction("shearAllLayers"); + connect(action, SIGNAL(triggered()), this, SLOT(slotShearAllLayers())= ); } = ShearImage::~ShearImage() @@ -50,9 +54,10 @@ ShearImage::~ShearImage() void ShearImage::slotShearImage() { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = + if (!viewManager()->blockUntilOperationsFinished(image)) return; + DlgShearImage * dlgShearImage =3D new DlgShearImage(viewManager()->mai= nWindow(), "ShearImage"); Q_CHECK_PTR(dlgShearImage); = @@ -66,12 +71,13 @@ void ShearImage::slotShearImage() delete dlgShearImage; } = -void ShearImage::slotShearLayer() +void ShearImage::shearLayerImpl(KisNodeSP rootNode) { KisImageWSP image =3D viewManager()->image(); - if (!image) return; = + if (!viewManager()->blockUntilOperationsFinished(image)) return; + DlgShearImage * dlgShearImage =3D new DlgShearImage(viewManager()->mai= nWindow(), "ShearLayer"); Q_CHECK_PTR(dlgShearImage); = @@ -80,10 +86,25 @@ void ShearImage::slotShearLayer() if (dlgShearImage->exec() =3D=3D QDialog::Accepted) { qint32 angleX =3D dlgShearImage->angleX(); qint32 angleY =3D dlgShearImage->angleY(); - viewManager()->nodeManager()->shear(angleX, angleY); = + image->shearNode(rootNode, + angleX, angleY, + viewManager()->selection()); } delete dlgShearImage; } = +void ShearImage::slotShearLayer() +{ + shearLayerImpl(viewManager()->activeNode()); +} + +void ShearImage::slotShearAllLayers() +{ + KisImageWSP image =3D viewManager()->image(); + if (!image) return; + + shearLayerImpl(image->root()); +} + #include "shearimage.moc" diff --git a/plugins/extensions/shearimage/shearimage.h b/plugins/extension= s/shearimage/shearimage.h index 49487245135..452958849cc 100644 --- a/plugins/extensions/shearimage/shearimage.h +++ b/plugins/extensions/shearimage/shearimage.h @@ -23,6 +23,8 @@ #include = #include +#include "kis_types.h" + = class ShearImage : public KisActionPlugin { @@ -31,10 +33,14 @@ public: ShearImage(QObject *parent, const QVariantList &); ~ShearImage() override; = +private: + void shearLayerImpl(KisNodeSP rootNode); + private Q_SLOTS: = void slotShearImage(); void slotShearLayer(); + void slotShearAllLayers(); }; = #endif // SHEARIMAGE_H diff --git a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp b/plug= ins/tools/tool_smart_patch/kis_tool_smart_patch.cpp index 656ba388cab..b969bcd7eab 100644 --- a/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp +++ b/plugins/tools/tool_smart_patch/kis_tool_smart_patch.cpp @@ -31,7 +31,7 @@ = #include "kundo2magicstring.h" #include "kundo2stack.h" -#include "kis_transaction_based_command.h" +#include "commands_new/kis_transaction_based_command.h" #include "kis_transaction.h" = #include "kis_processing_applicator.h"