diff --git a/libs/image/CMakeLists.txt b/libs/image/CMakeLists.txt index 6b0bfbe20e..6d7c6a4daa 100644 --- a/libs/image/CMakeLists.txt +++ b/libs/image/CMakeLists.txt @@ -1,381 +1,382 @@ add_subdirectory( tests ) add_subdirectory( tiles3 ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty ${CMAKE_CURRENT_SOURCE_DIR}/brushengine ${CMAKE_CURRENT_SOURCE_DIR}/commands ${CMAKE_CURRENT_SOURCE_DIR}/commands_new ${CMAKE_CURRENT_SOURCE_DIR}/filter ${CMAKE_CURRENT_SOURCE_DIR}/floodfill ${CMAKE_CURRENT_SOURCE_DIR}/generator ${CMAKE_CURRENT_SOURCE_DIR}/layerstyles ${CMAKE_CURRENT_SOURCE_DIR}/processing ${CMAKE_SOURCE_DIR}/sdk/tests ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ) if(FFTW3_FOUND) include_directories(${FFTW3_INCLUDE_DIR}) endif() if(HAVE_VC) include_directories(SYSTEM ${Vc_INCLUDE_DIR} ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS}) ko_compile_for_all_implementations(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) else() set(__per_arch_circle_mask_generator_objs kis_brush_mask_applicator_factories.cpp) endif() set(kritaimage_LIB_SRCS tiles3/kis_tile.cc tiles3/kis_tile_data.cc tiles3/kis_tile_data_store.cc tiles3/kis_tile_data_pooler.cc tiles3/kis_tiled_data_manager.cc tiles3/KisTiledExtentManager.cpp tiles3/kis_memento_manager.cc tiles3/kis_hline_iterator.cpp tiles3/kis_vline_iterator.cpp tiles3/kis_random_accessor.cc tiles3/swap/kis_abstract_compression.cpp tiles3/swap/kis_lzf_compression.cpp tiles3/swap/kis_abstract_tile_compressor.cpp tiles3/swap/kis_legacy_tile_compressor.cpp tiles3/swap/kis_tile_compressor_2.cpp tiles3/swap/kis_chunk_allocator.cpp tiles3/swap/kis_memory_window.cpp tiles3/swap/kis_swapped_data_store.cpp tiles3/swap/kis_tile_data_swapper.cpp kis_distance_information.cpp kis_painter.cc kis_painter_blt_multi_fixed.cpp kis_marker_painter.cpp KisPrecisePaintDeviceWrapper.cpp kis_progress_updater.cpp brushengine/kis_paint_information.cc brushengine/kis_random_source.cpp brushengine/KisPerStrokeRandomSource.cpp brushengine/kis_stroke_random_source.cpp brushengine/kis_paintop.cc brushengine/kis_paintop_factory.cpp brushengine/kis_paintop_preset.cpp brushengine/kis_paintop_registry.cc brushengine/kis_paintop_settings.cpp brushengine/kis_paintop_settings_update_proxy.cpp brushengine/kis_paintop_utils.cpp brushengine/kis_no_size_paintop_settings.cpp brushengine/kis_locked_properties.cc brushengine/kis_locked_properties_proxy.cpp brushengine/kis_locked_properties_server.cpp brushengine/kis_paintop_config_widget.cpp brushengine/kis_uniform_paintop_property.cpp brushengine/kis_combo_based_paintop_property.cpp brushengine/kis_slider_based_paintop_property.cpp brushengine/kis_standard_uniform_properties_factory.cpp brushengine/KisStrokeSpeedMeasurer.cpp brushengine/KisPaintopSettingsIds.cpp commands/kis_deselect_global_selection_command.cpp commands/KisDeselectActiveSelectionCommand.cpp commands/kis_image_change_layers_command.cpp commands/kis_image_change_visibility_command.cpp commands/kis_image_command.cpp commands/kis_image_layer_add_command.cpp commands/kis_image_layer_move_command.cpp commands/kis_image_layer_remove_command.cpp commands/kis_image_layer_remove_command_impl.cpp commands/kis_image_lock_command.cpp commands/kis_node_command.cpp commands/kis_node_compositeop_command.cpp commands/kis_node_opacity_command.cpp commands/kis_node_property_list_command.cpp commands/kis_reselect_global_selection_command.cpp commands/KisReselectActiveSelectionCommand.cpp commands/kis_set_global_selection_command.cpp commands/KisNodeRenameCommand.cpp commands_new/kis_saved_commands.cpp commands_new/kis_processing_command.cpp commands_new/kis_image_resize_command.cpp commands_new/kis_image_set_resolution_command.cpp commands_new/kis_node_move_command2.cpp commands_new/kis_set_layer_style_command.cpp commands_new/kis_selection_move_command2.cpp commands_new/kis_update_command.cpp 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 commands_new/KisHoldUIUpdatesCommand.cpp commands_new/KisChangeChannelFlagsCommand.cpp commands_new/KisChangeChannelLockFlagsCommand.cpp processing/kis_do_nothing_processing_visitor.cpp processing/kis_simple_processing_visitor.cpp processing/kis_convert_color_space_processing_visitor.cpp processing/kis_assign_profile_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 filter/kis_color_transformation_configuration.cc filter/kis_filter_registry.cc filter/kis_color_transformation_filter.cc generator/kis_generator.cpp generator/kis_generator_layer.cpp generator/kis_generator_registry.cpp floodfill/kis_fill_interval_map.cpp floodfill/kis_scanline_fill.cpp lazybrush/kis_min_cut_worker.cpp lazybrush/kis_lazy_fill_tools.cpp lazybrush/kis_multiway_cut.cpp lazybrush/KisWatershedWorker.cpp lazybrush/kis_colorize_mask.cpp lazybrush/kis_colorize_stroke_strategy.cpp KisDelayedUpdateNodeInterface.cpp KisCroppedOriginalLayerInterface.cpp + KisDecoratedNodeInterface.cpp kis_adjustment_layer.cc kis_selection_based_layer.cpp kis_node_filter_interface.cpp kis_base_accessor.cpp kis_base_node.cpp kis_base_processor.cpp kis_bookmarked_configuration_manager.cc kis_node_uuid_info.cpp kis_clone_layer.cpp kis_config_widget.cpp kis_convolution_kernel.cc kis_convolution_painter.cc kis_gaussian_kernel.cpp kis_edge_detection_kernel.cpp kis_cubic_curve.cpp kis_default_bounds.cpp kis_default_bounds_base.cpp kis_effect_mask.cc kis_fast_math.cpp kis_fill_painter.cc kis_filter_mask.cpp kis_filter_strategy.cc kis_transform_mask.cpp kis_transform_mask_params_interface.cpp kis_recalculate_transform_mask_job.cpp kis_recalculate_generator_layer_job.cpp kis_transform_mask_params_factory_registry.cpp kis_safe_transform.cpp kis_gradient_painter.cc kis_gradient_shape_strategy.cpp kis_cached_gradient_shape_strategy.cpp kis_polygonal_gradient_shape_strategy.cpp kis_iterator_ng.cpp kis_async_merger.cpp kis_merge_walker.cc kis_updater_context.cpp kis_update_job_item.cpp kis_stroke_strategy_undo_command_based.cpp kis_simple_stroke_strategy.cpp KisRunnableBasedStrokeStrategy.cpp KisRunnableStrokeJobDataBase.cpp KisRunnableStrokeJobData.cpp KisRunnableStrokeJobsInterface.cpp KisFakeRunnableStrokeJobsExecutor.cpp kis_stroke_job_strategy.cpp kis_stroke_strategy.cpp kis_stroke.cpp kis_strokes_queue.cpp KisStrokesQueueMutatedJobInterface.cpp kis_simple_update_queue.cpp kis_update_scheduler.cpp kis_queues_progress_updater.cpp kis_composite_progress_proxy.cpp kis_sync_lod_cache_stroke_strategy.cpp kis_lod_capable_layer_offset.cpp kis_update_time_monitor.cpp KisImageConfigNotifier.cpp kis_group_layer.cc kis_count_visitor.cpp kis_histogram.cc kis_image_interfaces.cpp kis_image_animation_interface.cpp kis_time_range.cpp kis_node_graph_listener.cpp kis_image.cc kis_image_signal_router.cpp KisImageSignals.cpp kis_image_config.cpp kis_projection_updates_filter.cpp kis_suspend_projection_updates_stroke_strategy.cpp kis_regenerate_frame_stroke_strategy.cpp kis_switch_time_stroke_strategy.cpp kis_crop_saved_extra_data.cpp kis_timed_signal_threshold.cpp kis_layer.cc kis_indirect_painting_support.cpp kis_abstract_projection_plane.cpp kis_layer_projection_plane.cpp kis_layer_utils.cpp kis_mask_projection_plane.cpp kis_projection_leaf.cpp KisSafeNodeProjectionStore.cpp kis_mask.cc kis_base_mask_generator.cpp kis_rect_mask_generator.cpp kis_circle_mask_generator.cpp kis_gauss_circle_mask_generator.cpp kis_gauss_rect_mask_generator.cpp ${__per_arch_circle_mask_generator_objs} kis_curve_circle_mask_generator.cpp kis_curve_rect_mask_generator.cpp kis_math_toolbox.cpp kis_memory_statistics_server.cpp kis_name_server.cpp kis_node.cpp kis_node_facade.cpp kis_node_progress_proxy.cpp kis_busy_progress_indicator.cpp kis_node_visitor.cpp kis_paint_device.cc kis_paint_device_debug_utils.cpp kis_fixed_paint_device.cpp KisOptimizedByteArray.cpp kis_paint_layer.cc kis_perspective_math.cpp kis_pixel_selection.cpp kis_processing_information.cpp kis_properties_configuration.cc kis_random_accessor_ng.cpp kis_random_generator.cc kis_random_sub_accessor.cpp kis_wrapped_random_accessor.cpp kis_selection.cc KisSelectionUpdateCompressor.cpp kis_selection_mask.cpp kis_update_outline_job.cpp kis_update_selection_job.cpp kis_serializable_configuration.cc kis_transaction_data.cpp kis_transform_worker.cc kis_perspectivetransform_worker.cpp bsplines/kis_bspline_1d.cpp bsplines/kis_bspline_2d.cpp bsplines/kis_nu_bspline_2d.cpp kis_warptransform_worker.cc kis_cage_transform_worker.cpp kis_liquify_transform_worker.cpp kis_green_coordinates_math.cpp kis_transparency_mask.cc kis_undo_adapter.cpp kis_macro_based_undo_store.cpp kis_surrogate_undo_adapter.cpp kis_legacy_undo_adapter.cpp kis_post_execution_undo_adapter.cpp kis_processing_visitor.cpp kis_processing_applicator.cpp krita_utils.cpp kis_outline_generator.cpp kis_layer_composition.cpp kis_selection_filters.cpp KisProofingConfiguration.h KisRecycleProjectionsJob.cpp kis_keyframe.cpp kis_keyframe_channel.cpp kis_keyframe_commands.cpp kis_scalar_keyframe_channel.cpp kis_raster_keyframe_channel.cpp kis_onion_skin_compositor.cpp kis_onion_skin_cache.cpp kis_idle_watcher.cpp kis_psd_layer_style.cpp kis_layer_properties_icons.cpp layerstyles/kis_multiple_projection.cpp layerstyles/kis_layer_style_filter.cpp layerstyles/kis_layer_style_filter_environment.cpp layerstyles/kis_layer_style_filter_projection_plane.cpp layerstyles/kis_layer_style_projection_plane.cpp layerstyles/kis_ls_drop_shadow_filter.cpp layerstyles/kis_ls_satin_filter.cpp layerstyles/kis_ls_stroke_filter.cpp layerstyles/kis_ls_bevel_emboss_filter.cpp layerstyles/kis_ls_overlay_filter.cpp layerstyles/kis_ls_utils.cpp layerstyles/gimp_bump_map.cpp KisProofingConfiguration.cpp kis_node_query_path.cc ) set(einspline_SRCS 3rdparty/einspline/bspline_create.cpp 3rdparty/einspline/bspline_data.cpp 3rdparty/einspline/multi_bspline_create.cpp 3rdparty/einspline/nubasis.cpp 3rdparty/einspline/nubspline_create.cpp 3rdparty/einspline/nugrid.cpp ) add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS}) generate_export_header(kritaimage BASE_NAME kritaimage) target_link_libraries(kritaimage PUBLIC kritaversion kritawidgets kritaglobal kritapsd kritaodf kritapigment kritacommand kritawidgetutils kritametadata Qt5::Concurrent ) target_link_libraries(kritaimage PUBLIC ${Boost_SYSTEM_LIBRARY}) if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) target_link_libraries(kritaimage PUBLIC atomic) endif() endif() if(OPENEXR_FOUND) target_link_libraries(kritaimage PUBLIC ${OPENEXR_LIBRARIES}) endif() if(FFTW3_FOUND) target_link_libraries(kritaimage PRIVATE ${FFTW3_LIBRARIES}) endif() if(HAVE_VC) target_link_libraries(kritaimage PUBLIC ${Vc_LIBRARIES}) endif() if (NOT GSL_FOUND) message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.") else () target_link_libraries(kritaimage PRIVATE ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES}) endif () target_include_directories(kritaimage PUBLIC $ $ $ $ $ ) set_target_properties(kritaimage PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaimage ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libs/image/KisDecoratedNodeInterface.cpp b/libs/image/KisDecoratedNodeInterface.cpp new file mode 100644 index 0000000000..b67179b8d4 --- /dev/null +++ b/libs/image/KisDecoratedNodeInterface.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "KisDecoratedNodeInterface.h" + +KisDecoratedNodeInterface::~KisDecoratedNodeInterface() +{ +} + +void KisDecoratedNodeInterface::setDecorationsVisible(bool value) +{ + setDecorationsVisible(value, true); +} diff --git a/libs/image/KisDecoratedNodeInterface.h b/libs/image/KisDecoratedNodeInterface.h new file mode 100644 index 0000000000..c79da00604 --- /dev/null +++ b/libs/image/KisDecoratedNodeInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KISDECORATEDNODEINTERFACE_H +#define KISDECORATEDNODEINTERFACE_H + +#include "kritaimage_export.h" + +/** + * A special interface for layer that have a "decorations", + * that is, a data that is written into layer stack, but is + * not a part of user's image. + */ +class KRITAIMAGE_EXPORT KisDecoratedNodeInterface +{ +public: + virtual ~KisDecoratedNodeInterface(); + + /** + * \return true is the layer is allowed to write + * its decorative information into the stack. The + * value should be "true" by default. + */ + virtual bool decorationsVisible() const = 0; + + /** + * Enable or disable writing decorative information into + * layer stack. + */ + virtual void setDecorationsVisible(bool value, bool update) = 0; + + /** + * Convenience override for setDecorationsVisible() + */ + void setDecorationsVisible(bool value); +}; + +#endif // KISDECORATEDNODEINTERFACE_H diff --git a/libs/image/kis_paint_layer.cc b/libs/image/kis_paint_layer.cc index ba3210a97f..08a6048c04 100644 --- a/libs/image/kis_paint_layer.cc +++ b/libs/image/kis_paint_layer.cc @@ -1,347 +1,368 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2006 Bart Coppens * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_paint_layer.h" #include #include #include #include #include #include #include #include #include "kis_image.h" #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_default_bounds.h" #include "kis_onion_skin_compositor.h" #include "kis_raster_keyframe_channel.h" #include "kis_signal_auto_connection.h" #include "kis_layer_properties_icons.h" #include "kis_onion_skin_cache.h" struct Q_DECL_HIDDEN KisPaintLayer::Private { public: Private() : contentChannel(0) {} KisPaintDeviceSP paintDevice; QBitArray paintChannelFlags; // the real pointer is owned by the paint device KisRasterKeyframeChannel *contentChannel; KisSignalAutoConnectionsStore onionSkinConnection; KisOnionSkinCache onionSkinCache; + + bool onionSkinVisibleOverride = true; }; KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev) : KisLayer(image, name, opacity) , m_d(new Private()) { Q_ASSERT(dev); init(dev); m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); } KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity) : KisLayer(image, name, opacity) , m_d(new Private()) { Q_ASSERT(image); init(new KisPaintDevice(this, image->colorSpace(), new KisDefaultBounds(image))); } KisPaintLayer::KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace) : KisLayer(image, name, opacity) , m_d(new Private()) { if (!colorSpace) { Q_ASSERT(image); colorSpace = image->colorSpace(); } Q_ASSERT(colorSpace); init(new KisPaintDevice(this, colorSpace, new KisDefaultBounds(image))); } KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs) : KisLayer(rhs) , KisIndirectPaintingSupport() , m_d(new Private) { const bool copyFrames = (rhs.m_d->contentChannel != 0); if (!copyFrames) { init(new KisPaintDevice(*rhs.m_d->paintDevice.data()), rhs.m_d->paintChannelFlags); } else { init(new KisPaintDevice(*rhs.m_d->paintDevice.data(), KritaUtils::CopyAllFrames), rhs.m_d->paintChannelFlags); m_d->contentChannel = m_d->paintDevice->keyframeChannel(); addKeyframeChannel(m_d->contentChannel); m_d->contentChannel->setOnionSkinsEnabled(rhs.onionSkinEnabled()); KisLayer::enableAnimation(); } } void KisPaintLayer::init(KisPaintDeviceSP paintDevice, const QBitArray &paintChannelFlags) { m_d->paintDevice = paintDevice; m_d->paintDevice->setParentNode(this); m_d->paintChannelFlags = paintChannelFlags; } KisPaintLayer::~KisPaintLayer() { delete m_d; } bool KisPaintLayer::allowAsChild(KisNodeSP node) const { return node->inherits("KisMask"); } KisPaintDeviceSP KisPaintLayer::original() const { return m_d->paintDevice; } KisPaintDeviceSP KisPaintLayer::paintDevice() const { return m_d->paintDevice; } bool KisPaintLayer::needProjection() const { return hasTemporaryTarget() || (isAnimated() && onionSkinEnabled()); } void KisPaintLayer::copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const { KisIndirectPaintingSupport::ReadLocker l(this); KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect); if (hasTemporaryTarget()) { KisPainter gc(projection); setupTemporaryPainter(&gc); gc.bitBlt(rect.topLeft(), temporaryTarget(), rect); } if (m_d->contentChannel && m_d->contentChannel->keyframeCount() > 1 && onionSkinEnabled() && + m_d->onionSkinVisibleOverride && !m_d->paintDevice->defaultBounds()->externalFrameActive()) { KisPaintDeviceSP skins = m_d->onionSkinCache.projection(m_d->paintDevice); KisPainter gcDest(projection); gcDest.setCompositeOp(m_d->paintDevice->colorSpace()->compositeOp(COMPOSITE_BEHIND)); gcDest.bitBlt(rect.topLeft(), skins, rect); gcDest.end(); } if (!m_d->contentChannel || (m_d->contentChannel->keyframeCount() <= 1) || !onionSkinEnabled()) { m_d->onionSkinCache.reset(); } } QIcon KisPaintLayer::icon() const { return KisIconUtils::loadIcon("paintLayer"); } void KisPaintLayer::setImage(KisImageWSP image) { m_d->paintDevice->setDefaultBounds(new KisDefaultBounds(image)); KisLayer::setImage(image); } KisBaseNode::PropertyList KisPaintLayer::sectionModelProperties() const { KisBaseNode::PropertyList l = KisLayer::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::alphaLocked, alphaLocked()); if (isAnimated()) { l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::onionSkins, onionSkinEnabled()); } return l; } void KisPaintLayer::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { Q_FOREACH (const KisBaseNode::Property &property, properties) { if (property.name == i18n("Alpha Locked")) { setAlphaLocked(property.state.toBool()); } else if (property.name == i18n("Onion Skins")) { setOnionSkinEnabled(property.state.toBool()); } } KisLayer::setSectionModelProperties(properties); } const KoColorSpace * KisPaintLayer::colorSpace() const { return m_d->paintDevice->colorSpace(); } bool KisPaintLayer::accept(KisNodeVisitor &v) { return v.visit(this); } void KisPaintLayer::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } void KisPaintLayer::setChannelLockFlags(const QBitArray& channelFlags) { Q_ASSERT(((quint32)channelFlags.count() == colorSpace()->channelCount() || channelFlags.isEmpty())); m_d->paintChannelFlags = channelFlags; } const QBitArray& KisPaintLayer::channelLockFlags() const { return m_d->paintChannelFlags; } QRect KisPaintLayer::extent() const { KisPaintDeviceSP t = temporaryTarget(); QRect rect = t ? t->extent() : QRect(); - if (onionSkinEnabled()) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); + if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); return rect | KisLayer::extent(); } QRect KisPaintLayer::exactBounds() const { KisPaintDeviceSP t = temporaryTarget(); QRect rect = t ? t->extent() : QRect(); - if (onionSkinEnabled()) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); + if (onionSkinEnabled() && m_d->onionSkinVisibleOverride) rect |= KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice); return rect | KisLayer::exactBounds(); } bool KisPaintLayer::alphaLocked() const { QBitArray flags = colorSpace()->channelFlags(false, true) & m_d->paintChannelFlags; return flags.count(true) == 0 && !m_d->paintChannelFlags.isEmpty(); } void KisPaintLayer::setAlphaLocked(bool lock) { if(m_d->paintChannelFlags.isEmpty()) m_d->paintChannelFlags = colorSpace()->channelFlags(true, true); if(lock) m_d->paintChannelFlags &= colorSpace()->channelFlags(true, false); else m_d->paintChannelFlags |= colorSpace()->channelFlags(false, true); baseNodeChangedCallback(); } bool KisPaintLayer::onionSkinEnabled() const { return nodeProperties().boolProperty("onionskin", false); } void KisPaintLayer::setOnionSkinEnabled(bool state) { int oldState = onionSkinEnabled(); if (oldState == state) return; if (state == false && oldState) { // FIXME: change ordering! race condition possible! // Turning off onionskins shrinks our extent. Let's clean up the onion skins first setDirty(KisOnionSkinCompositor::instance()->calculateExtent(m_d->paintDevice)); } if (state) { m_d->onionSkinConnection.addConnection(KisOnionSkinCompositor::instance(), SIGNAL(sigOnionSkinChanged()), this, SLOT(slotExternalUpdateOnionSkins())); } else { m_d->onionSkinConnection.clear(); } if (m_d->contentChannel) { m_d->contentChannel->setOnionSkinsEnabled(state); } setNodeProperty("onionskin", state); } void KisPaintLayer::slotExternalUpdateOnionSkins() { if (!onionSkinEnabled()) return; const QRect dirtyRect = KisOnionSkinCompositor::instance()->calculateFullExtent(m_d->paintDevice); setDirty(dirtyRect); } KisKeyframeChannel *KisPaintLayer::requestKeyframeChannel(const QString &id) { if (id == KisKeyframeChannel::Content.id()) { m_d->contentChannel = m_d->paintDevice->createKeyframeChannel(KisKeyframeChannel::Content); m_d->contentChannel->setOnionSkinsEnabled(onionSkinEnabled()); enableAnimation(); return m_d->contentChannel; } return KisLayer::requestKeyframeChannel(id); } KisPaintDeviceList KisPaintLayer::getLodCapableDevices() const { KisPaintDeviceList list = KisLayer::getLodCapableDevices(); KisPaintDeviceSP onionSkinsDevice = m_d->onionSkinCache.lodCapableDevice(); if (onionSkinsDevice) { list << onionSkinsDevice; } return list; } + +bool KisPaintLayer::decorationsVisible() const +{ + return m_d->onionSkinVisibleOverride; +} + +void KisPaintLayer::setDecorationsVisible(bool value, bool update) +{ + if (value == decorationsVisible()) return; + + const QRect oldExtent = extent(); + + m_d->onionSkinVisibleOverride = value; + + if (update) { + setDirty(oldExtent | extent()); + } +} diff --git a/libs/image/kis_paint_layer.h b/libs/image/kis_paint_layer.h index d35673034c..ef442389e6 100644 --- a/libs/image/kis_paint_layer.h +++ b/libs/image/kis_paint_layer.h @@ -1,177 +1,182 @@ /* * Copyright (c) 2005 C. Boemann * Copyright (c) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINT_LAYER_H_ #define KIS_PAINT_LAYER_H_ #include "kis_types.h" #include "kis_layer.h" #include "kis_indirect_painting_support.h" +#include "KisDecoratedNodeInterface.h" #include class KoColorSpace; /** * This layer is of a type that can be drawn on. A paint layer can * have any number of effect masks, a transparency mask, a local * selection and a protection mask. * * The protection mask can be read/write, read-only or write-only. * The transparency mask has two rendering forms: as a selection mask * and by changing the transparency of the paint layer's pixels. */ -class KRITAIMAGE_EXPORT KisPaintLayer : public KisLayer, public KisIndirectPaintingSupport +class KRITAIMAGE_EXPORT KisPaintLayer : public KisLayer, public KisIndirectPaintingSupport, public KisDecoratedNodeInterface { Q_OBJECT public: /** * Construct a paint layer with the given parameters. The default bounds of the paintdevice are overwritten. * @param image this layer belongs to, or null, if it shouldn't belong to any image * @param name of the layer * @param opacity in the range between OPACITY_TRANSPARENT_U8 and OPACITY_OPAQUE_U8 * @param dev is the paint device, that should be used */ KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, KisPaintDeviceSP dev); /** * Construct a paint layer with the given parameters * @param image this layer belongs to. it must not be null and it must have a valid color space. * @param name of the layer * @param opacity in the range between OPACITY_TRANSPARENT_U8 and OPACITY_OPAQUE_U8 */ KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity); /** * Construct a paint layer with the given parameters * @param image this layer belongs to, or null, if it shouldn't belong to any image. image must not be null, if colorSpace is null * @param name of the layer * @param opacity in the range between OPACITY_TRANSPARENT_U8 and OPACITY_OPAQUE_U8 * @param colorSpace is the color space, that should be used to construct the paint device. it can be null, if the image is valid. */ KisPaintLayer(KisImageWSP image, const QString& name, quint8 opacity, const KoColorSpace * colorSpace); /** * Copy Constructor */ KisPaintLayer(const KisPaintLayer& rhs); ~KisPaintLayer() override; KisNodeSP clone() const override { return KisNodeSP(new KisPaintLayer(*this)); } bool allowAsChild(KisNodeSP) const override; const KoColorSpace * colorSpace() const override; bool needProjection() const override; QIcon icon() const override; void setImage(KisImageWSP image) override; KisBaseNode::PropertyList sectionModelProperties() const override; void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override; public: QRect extent() const override; QRect exactBounds() const override; bool accept(KisNodeVisitor &v) override; void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override; /** * set the channelflags for locking certain channels (used by painting tools) * for this layer to the specified bit array. * The bit array must have exactly the same number of channels as * the colorspace this layer is in, or be empty, in which case all * channels are active. */ void setChannelLockFlags(const QBitArray& channelFlags); /** * Return a bit array where each bit indicates whether a * particular channel is locked or not (used by painting tools). * If the channelflags bit array is empty, all channels are active. */ const QBitArray& channelLockFlags() const; /** * Returns the paintDevice that accompanies this layer */ KisPaintDeviceSP paintDevice() const override; /** * Returns the original pixels before masks have been applied. */ KisPaintDeviceSP original() const override; /** * @returns true when painting should not affect the alpha channel */ bool alphaLocked() const; /** * @param l if true, the alpha channel will be protected from modification */ void setAlphaLocked(bool lock); /** * @return true if onion skins should be rendered on this layer */ bool onionSkinEnabled() const; /** * @param state whether onion skins should be rendered */ void setOnionSkinEnabled(bool state); KisPaintDeviceList getLodCapableDevices() const override; + bool decorationsVisible() const override; + void setDecorationsVisible(bool value, bool update) override; + using KisDecoratedNodeInterface::setDecorationsVisible; + public Q_SLOTS: void slotExternalUpdateOnionSkins(); public: // KisIndirectPaintingSupport KisLayer* layer() { return this; } protected: // override from KisLayer void copyOriginalToProjection(const KisPaintDeviceSP original, KisPaintDeviceSP projection, const QRect& rect) const override; KisKeyframeChannel *requestKeyframeChannel(const QString &id) override; private: void init(KisPaintDeviceSP paintDevice, const QBitArray &paintChannelFlags = QBitArray()); struct Private; Private * const m_d; }; typedef KisSharedPtr KisPaintLayerSP; #endif // KIS_PAINT_LAYER_H_ diff --git a/libs/image/kis_selection_mask.cpp b/libs/image/kis_selection_mask.cpp index 73435a95e1..49adca417d 100644 --- a/libs/image/kis_selection_mask.cpp +++ b/libs/image/kis_selection_mask.cpp @@ -1,329 +1,347 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_mask.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_selection.h" #include #include #include #include "kis_fill_painter.h" #include #include "kis_node_visitor.h" #include "kis_processing_visitor.h" #include "kis_pixel_selection.h" #include "kis_undo_adapter.h" #include #include #include "kis_thread_safe_signal_compressor.h" #include "kis_layer_properties_icons.h" #include "kis_cached_paint_device.h" #include "kis_image_config.h" #include "KisImageConfigNotifier.h" struct Q_DECL_HIDDEN KisSelectionMask::Private { public: Private(KisSelectionMask *_q) : q(_q) , updatesCompressor(0) , maskColor(Qt::green, KoColorSpaceRegistry::instance()->rgb8()) {} KisSelectionMask *q; KisImageWSP image; KisCachedPaintDevice paintDeviceCache; KisCachedSelection cachedSelection; KisThreadSafeSignalCompressor *updatesCompressor; KoColor maskColor; void slotSelectionChangedCompressed(); void slotConfigChangedImpl(bool blockUpdates); void slotConfigChanged(); }; KisSelectionMask::KisSelectionMask(KisImageWSP image) : KisEffectMask() , m_d(new Private(this)) { setName("selection"); setActive(false); setSupportsLodMoves(false); m_d->image = image; m_d->updatesCompressor = new KisThreadSafeSignalCompressor(50, KisSignalCompressor::FIRST_ACTIVE); connect(m_d->updatesCompressor, SIGNAL(timeout()), SLOT(slotSelectionChangedCompressed())); this->moveToThread(image->thread()); connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); m_d->slotConfigChangedImpl(false); } KisSelectionMask::KisSelectionMask(const KisSelectionMask& rhs) : KisEffectMask(rhs) , m_d(new Private(this)) { m_d->image = rhs.image(); m_d->updatesCompressor = new KisThreadSafeSignalCompressor(300, KisSignalCompressor::POSTPONE); connect(m_d->updatesCompressor, SIGNAL(timeout()), SLOT(slotSelectionChangedCompressed())); this->moveToThread(m_d->image->thread()); connect(KisImageConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); m_d->slotConfigChangedImpl(false); } KisSelectionMask::~KisSelectionMask() { m_d->updatesCompressor->deleteLater(); delete m_d; } QIcon KisSelectionMask::icon() const { return KisIconUtils::loadIcon("selectionMask"); } void KisSelectionMask::mergeInMaskInternal(KisPaintDeviceSP projection, KisSelectionSP effectiveSelection, const QRect &applyRect, const QRect &preparedNeedRect, KisNode::PositionToFilthy maskPos) const { Q_UNUSED(maskPos); Q_UNUSED(preparedNeedRect); if (!effectiveSelection) return; { KisSelectionSP mainMaskSelection = this->selection(); if (mainMaskSelection && (!mainMaskSelection->isVisible() || mainMaskSelection->pixelSelection()->defaultBounds()->externalFrameActive())) { return; } } KisPaintDeviceSP fillDevice = m_d->paintDeviceCache.getDevice(projection); fillDevice->setDefaultPixel(m_d->maskColor); const QRect selectionExtent = effectiveSelection->selectedRect(); if (selectionExtent.contains(applyRect) || selectionExtent.intersects(applyRect)) { KisSelectionSP invertedSelection = m_d->cachedSelection.getSelection(); invertedSelection->pixelSelection()->makeCloneFromRough(effectiveSelection->pixelSelection(), applyRect); invertedSelection->pixelSelection()->invert(); KisPainter gc(projection); gc.setSelection(invertedSelection); gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect); m_d->cachedSelection.putSelection(invertedSelection); } else { KisPainter gc(projection); gc.bitBlt(applyRect.topLeft(), fillDevice, applyRect); } m_d->paintDeviceCache.putDevice(fillDevice); } bool KisSelectionMask::paintsOutsideSelection() const { return true; } void KisSelectionMask::setSelection(KisSelectionSP selection) { if (selection) { KisEffectMask::setSelection(selection); } else { KisEffectMask::setSelection(new KisSelection()); const KoColorSpace * cs = KoColorSpaceRegistry::instance()->alpha8(); KisFillPainter gc(KisPaintDeviceSP(this->selection()->pixelSelection().data())); gc.fillRect(image()->bounds(), KoColor(Qt::white, cs), MAX_SELECTED); gc.end(); } setDirty(); } KisImageWSP KisSelectionMask::image() const { return m_d->image; } bool KisSelectionMask::accept(KisNodeVisitor &v) { return v.visit(this); } void KisSelectionMask::accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) { return visitor.visit(this, undoAdapter); } KisBaseNode::PropertyList KisSelectionMask::sectionModelProperties() const { KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties(); l << KisLayerPropertiesIcons::getProperty(KisLayerPropertiesIcons::selectionActive, active()); return l; } void KisSelectionMask::setSectionModelProperties(const KisBaseNode::PropertyList &properties) { KisEffectMask::setSectionModelProperties(properties); setActive(properties.at(2).state.toBool()); } void KisSelectionMask::setVisible(bool visible, bool isLoading) { const bool oldVisible = this->visible(false); setNodeProperty("visible", visible); if (!isLoading && visible != oldVisible) { if (selection()) selection()->setVisible(visible); } } bool KisSelectionMask::active() const { return nodeProperties().boolProperty("active", true); } void KisSelectionMask::setActive(bool active) { KisImageWSP image = this->image(); KisLayerSP parentLayer = qobject_cast(parent().data()); if (active && parentLayer) { KisSelectionMaskSP activeMask = parentLayer->selectionMask(); if (activeMask && activeMask != this) { activeMask->setActive(false); } } const bool oldActive = this->active(); setNodeProperty("active", active); /** * WARNING: we have a direct link to the image here, but we * must not use it for notification until we are a part of * the nore graph! Notifications should be emitted iff we * have graph listener link set up. */ if (graphListener() && image && oldActive != active) { baseNodeChangedCallback(); image->undoAdapter()->emitSelectionChanged(); } } QRect KisSelectionMask::needRect(const QRect &rect, KisNode::PositionToFilthy pos) const { Q_UNUSED(pos); // selection masks just add an overlay, so the needed rect is simply passed through return rect; } QRect KisSelectionMask::changeRect(const QRect &rect, KisNode::PositionToFilthy pos) const { Q_UNUSED(pos); // selection masks just add an overlay, so the changed rect is simply passed through return rect; } QRect KisSelectionMask::extent() const { // since mask overlay is inverted, the mask paints over // the entire image bounds QRect resultRect; KisSelectionSP selection = this->selection(); if (selection) { resultRect = selection->pixelSelection()->defaultBounds()->bounds(); } else if (KisNodeSP parent = this->parent()) { KisPaintDeviceSP dev = parent->projection(); if (dev) { resultRect = dev->defaultBounds()->bounds(); } } return resultRect; } QRect KisSelectionMask::exactBounds() const { return extent(); } void KisSelectionMask::notifySelectionChangedCompressed() { m_d->updatesCompressor->start(); } +bool KisSelectionMask::decorationsVisible() const +{ + return selection()->isVisible(); +} + +void KisSelectionMask::setDecorationsVisible(bool value, bool update) +{ + if (value == decorationsVisible()) return; + + const QRect oldExtent = extent(); + + selection()->setVisible(value); + + if (update) { + setDirty(oldExtent | extent()); + } +} + void KisSelectionMask::flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const { Q_UNUSED(selection); Q_UNUSED(dirtyRect); } void KisSelectionMask::Private::slotSelectionChangedCompressed() { KisSelectionSP currentSelection = q->selection(); if (!currentSelection) return; currentSelection->notifySelectionChanged(); } void KisSelectionMask::Private::slotConfigChangedImpl(bool doUpdates) { const KoColorSpace *cs = image ? image->colorSpace() : KoColorSpaceRegistry::instance()->rgb8(); KisImageConfig cfg(true); maskColor = KoColor(cfg.selectionOverlayMaskColor(), cs); if (doUpdates && image && image->overlaySelectionMask() == q) { q->setDirty(); } } void KisSelectionMask::Private::slotConfigChanged() { slotConfigChangedImpl(true); } #include "moc_kis_selection_mask.cpp" diff --git a/libs/image/kis_selection_mask.h b/libs/image/kis_selection_mask.h index 9fd6467cac..fc479a21a6 100644 --- a/libs/image/kis_selection_mask.h +++ b/libs/image/kis_selection_mask.h @@ -1,100 +1,105 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_SELECTION_MASK_ #define _KIS_SELECTION_MASK_ #include #include "kis_base_node.h" #include "kis_types.h" #include "kis_effect_mask.h" +#include "KisDecoratedNodeInterface.h" /** * An selection mask is a single channel mask that applies a * particular selection to the layer the mask belongs to. A selection * can contain both vector and pixel selection components. */ -class KRITAIMAGE_EXPORT KisSelectionMask : public KisEffectMask +class KRITAIMAGE_EXPORT KisSelectionMask : public KisEffectMask, public KisDecoratedNodeInterface { Q_OBJECT public: /** * Create an empty selection mask. There is filter and no layer * associated with this mask. */ KisSelectionMask(KisImageWSP image); ~KisSelectionMask() override; KisSelectionMask(const KisSelectionMask& rhs); QIcon icon() const override; KisNodeSP clone() const override { return KisNodeSP(new KisSelectionMask(*this)); } /// Set the selection of this adjustment layer to a copy of selection. void setSelection(KisSelectionSP selection); bool accept(KisNodeVisitor &v) override; void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter) override; KisBaseNode::PropertyList sectionModelProperties() const override; void setSectionModelProperties(const KisBaseNode::PropertyList &properties) override; void setVisible(bool visible, bool isLoading = false) override; bool active() const; void setActive(bool active); QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override; QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override; QRect extent() const override; QRect exactBounds() const override; /** * This method works like the one in KisSelection, but it * compressed the incoming events instead of processing each of * them separately. */ void notifySelectionChangedCompressed(); + bool decorationsVisible() const override; + void setDecorationsVisible(bool value, bool update) override; + using KisDecoratedNodeInterface::setDecorationsVisible; + protected: void flattenSelectionProjection(KisSelectionSP selection, const QRect &dirtyRect) const override; void mergeInMaskInternal(KisPaintDeviceSP projection, KisSelectionSP effectiveSelection, const QRect &applyRect, const QRect &preparedNeedRect, KisNode::PositionToFilthy maskPos) const override; bool paintsOutsideSelection() const override; private: Q_PRIVATE_SLOT(m_d, void slotSelectionChangedCompressed()) Q_PRIVATE_SLOT(m_d, void slotConfigChanged()) KisImageWSP image() const; struct Private; Private * const m_d; }; #endif //_KIS_SELECTION_MASK_ diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp index 9390384de0..42d405feae 100644 --- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp +++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.cpp @@ -1,697 +1,717 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "transform_stroke_strategy.h" #include #include "kundo2commandextradata.h" #include "kis_node_progress_proxy.h" #include #include #include #include #include #include #include #include #include "kis_transform_mask_adapter.h" #include "kis_transform_utils.h" #include "kis_abstract_projection_plane.h" #include "kis_recalculate_transform_mask_job.h" #include "kis_projection_leaf.h" #include "kis_modify_transform_mask_command.h" #include "kis_sequential_iterator.h" #include "kis_selection_mask.h" #include "kis_image_config.h" #include "kis_layer_utils.h" #include #include #include "transform_transaction_properties.h" #include "krita_container_utils.h" #include "commands_new/kis_saved_commands.h" #include "kis_command_ids.h" #include "KisRunnableStrokeJobUtils.h" #include "commands_new/KisHoldUIUpdatesCommand.h" +#include "KisDecoratedNodeInterface.h" TransformStrokeStrategy::TransformStrokeStrategy(ToolTransformArgs::TransformMode mode, bool workRecursively, const QString &filterId, bool forceReset, KisNodeSP rootNode, KisSelectionSP selection, KisStrokeUndoFacade *undoFacade, KisUpdatesFacade *updatesFacade) : KisStrokeStrategyUndoCommandBased(kundo2_i18n("Transform"), false, undoFacade), m_updatesFacade(updatesFacade), m_mode(mode), m_workRecursively(workRecursively), m_filterId(filterId), m_forceReset(forceReset), m_selection(selection) { KIS_SAFE_ASSERT_RECOVER_NOOP(!selection || !dynamic_cast(rootNode.data())); m_rootNode = rootNode; setMacroId(KisCommandUtils::TransformToolId); } TransformStrokeStrategy::~TransformStrokeStrategy() { } KisPaintDeviceSP TransformStrokeStrategy::createDeviceCache(KisPaintDeviceSP dev) { KisPaintDeviceSP cache; if (m_selection) { QRect srcRect = m_selection->selectedExactRect(); cache = dev->createCompositionSourceDevice(); KisPainter gc(cache); gc.setSelection(m_selection); gc.bitBlt(srcRect.topLeft(), dev, srcRect); } else { cache = dev->createCompositionSourceDevice(dev); } return cache; } bool TransformStrokeStrategy::haveDeviceInCache(KisPaintDeviceSP src) { QMutexLocker l(&m_devicesCacheMutex); return m_devicesCacheHash.contains(src.data()); } void TransformStrokeStrategy::putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache) { QMutexLocker l(&m_devicesCacheMutex); m_devicesCacheHash.insert(src.data(), cache); } KisPaintDeviceSP TransformStrokeStrategy::getDeviceCache(KisPaintDeviceSP src) { QMutexLocker l(&m_devicesCacheMutex); KisPaintDeviceSP cache = m_devicesCacheHash.value(src.data()); if (!cache) { warnKrita << "WARNING: Transform Stroke: the device is absent in cache!"; } return cache; } bool TransformStrokeStrategy::checkBelongsToSelection(KisPaintDeviceSP device) const { return m_selection && (device == m_selection->pixelSelection().data() || device == m_selection->projection().data()); } void TransformStrokeStrategy::doStrokeCallback(KisStrokeJobData *data) { TransformData *td = dynamic_cast(data); ClearSelectionData *csd = dynamic_cast(data); PreparePreviewData *ppd = dynamic_cast(data); TransformAllData *runAllData = dynamic_cast(data); if (runAllData) { // here we only save the passed args, actual // transformation will be performed during // finish job m_savedTransformArgs = runAllData->config; } else if (ppd) { KisNodeSP rootNode = m_rootNode; KisNodeList processedNodes = m_processedNodes; KisPaintDeviceSP previewDevice; if (rootNode->childCount() || !rootNode->paintDevice()) { if (KisTransformMask* tmask = dynamic_cast(rootNode.data())) { previewDevice = createDeviceCache(tmask->buildPreviewDevice()); KIS_SAFE_ASSERT_RECOVER(!m_selection) { m_selection = 0; } } else if (KisGroupLayer *group = dynamic_cast(rootNode.data())) { const QRect bounds = group->image()->bounds(); KisImageSP clonedImage = new KisImage(0, bounds.width(), bounds.height(), group->colorSpace(), "transformed_image"); KisGroupLayerSP clonedGroup = dynamic_cast(group->clone().data()); // In case the group is pass-through, it needs to be disabled for the preview, // otherwise it will crash (no parent for a preview leaf). // Also it needs to be done before setting the root layer for clonedImage. // Result: preview for pass-through group is the same as for standard group // (i.e. filter layers in the group won't affect the layer stack for a moment). clonedGroup->setPassThroughMode(false); clonedImage->setRootLayer(clonedGroup); QQueue linearizedSrcNodes; KisLayerUtils::recursiveApplyNodes(rootNode, [&linearizedSrcNodes] (KisNodeSP node) { linearizedSrcNodes.enqueue(node); }); KisLayerUtils::recursiveApplyNodes(KisNodeSP(clonedGroup), [&linearizedSrcNodes, processedNodes] (KisNodeSP node) { KisNodeSP srcNode = linearizedSrcNodes.dequeue(); if (!processedNodes.contains(srcNode)) { node->setVisible(false); } }); clonedImage->refreshGraph(); KisLayerUtils::refreshHiddenAreaAsync(clonedImage, clonedGroup, clonedImage->bounds()); KisLayerUtils::forceAllDelayedNodesUpdate(clonedGroup); clonedImage->waitForDone(); previewDevice = createDeviceCache(clonedImage->projection()); previewDevice->setDefaultBounds(group->projection()->defaultBounds()); // we delete the cloned image in GUI thread to ensure // no signals are still pending makeKisDeleteLaterWrapper(clonedImage)->deleteLater(); } else { rootNode->projectionLeaf()->explicitlyRegeneratePassThroughProjection(); previewDevice = createDeviceCache(rootNode->projection()); } } else { KisPaintDeviceSP cacheDevice = createDeviceCache(rootNode->paintDevice()); if (dynamic_cast(rootNode.data())) { KIS_SAFE_ASSERT_RECOVER (cacheDevice->colorSpace()->colorModelId() == GrayAColorModelID && cacheDevice->colorSpace()->colorDepthId() == Integer8BitsColorDepthID) { cacheDevice->convertTo(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id())); } previewDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); const QRect srcRect = cacheDevice->exactBounds(); KisSequentialConstIterator srcIt(cacheDevice, srcRect); KisSequentialIterator dstIt(previewDevice, srcRect); const int pixelSize = previewDevice->colorSpace()->pixelSize(); KisImageConfig cfg(true); KoColor pixel(cfg.selectionOverlayMaskColor(), previewDevice->colorSpace()); const qreal coeff = 1.0 / 255.0; const qreal baseOpacity = 0.5; while (srcIt.nextPixel() && dstIt.nextPixel()) { qreal gray = srcIt.rawDataConst()[0]; qreal alpha = srcIt.rawDataConst()[1]; pixel.setOpacity(quint8(gray * alpha * baseOpacity * coeff)); memcpy(dstIt.rawData(), pixel.data(), pixelSize); } } else { previewDevice = cacheDevice; } putDeviceCache(rootNode->paintDevice(), cacheDevice); } emit sigPreviewDeviceReady(previewDevice); } else if(td) { if (td->destination == TransformData::PAINT_DEVICE) { QRect oldExtent = td->node->extent(); KisPaintDeviceSP device = td->node->paintDevice(); if (device && !checkBelongsToSelection(device)) { KisPaintDeviceSP cachedPortion = getDeviceCache(device); Q_ASSERT(cachedPortion); KisTransaction transaction(device); KisProcessingVisitor::ProgressHelper helper(td->node); transformAndMergeDevice(td->config, cachedPortion, device, &helper); runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); td->node->setDirty(oldExtent | td->node->extent()); } else if (KisExternalLayer *extLayer = dynamic_cast(td->node.data())) { if (td->config.mode() == ToolTransformArgs::FREE_TRANSFORM || (td->config.mode() == ToolTransformArgs::PERSPECTIVE_4POINT && extLayer->supportsPerspectiveTransform())) { QVector3D transformedCenter; KisTransformWorker w = KisTransformUtils::createTransformWorker(td->config, 0, 0, &transformedCenter); QTransform t = w.transform(); runAndSaveCommand(KUndo2CommandSP(extLayer->transform(t)), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (KisTransformMask *transformMask = dynamic_cast(td->node.data())) { runAndSaveCommand(KUndo2CommandSP( new KisModifyTransformMaskCommand(transformMask, KisTransformMaskParamsInterfaceSP( new KisTransformMaskAdapter(td->config)))), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (m_selection) { /** * We use usual transaction here, because we cannot calsulate * transformation for perspective and warp workers. */ KisTransaction transaction(m_selection->pixelSelection()); KisProcessingVisitor::ProgressHelper helper(td->node); KisTransformUtils::transformDevice(td->config, m_selection->pixelSelection(), &helper); runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::CONCURRENT, KisStrokeJobData::NORMAL); } } else if (csd) { KisPaintDeviceSP device = csd->node->paintDevice(); if (device && !checkBelongsToSelection(device)) { if (!haveDeviceInCache(device)) { putDeviceCache(device, createDeviceCache(device)); } clearSelection(device); /** * Selection masks might have an overlay enabled, we should disable that */ if (KisSelectionMask *mask = dynamic_cast(csd->node.data())) { KisSelectionSP selection = mask->selection(); if (selection) { selection->setVisible(false); m_deactivatedSelections.append(selection); mask->setDirty(); } } } else if (KisExternalLayer *externalLayer = dynamic_cast(csd->node.data())) { externalLayer->projectionLeaf()->setTemporaryHiddenFromRendering(true); externalLayer->setDirty(); m_hiddenProjectionLeaves.append(csd->node); } else if (KisTransformMask *transformMask = dynamic_cast(csd->node.data())) { runAndSaveCommand(KUndo2CommandSP( new KisModifyTransformMaskCommand(transformMask, KisTransformMaskParamsInterfaceSP( new KisDumbTransformMaskParams(true)))), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } } else { KisStrokeStrategyUndoCommandBased::doStrokeCallback(data); } } void TransformStrokeStrategy::clearSelection(KisPaintDeviceSP device) { KisTransaction transaction(device); if (m_selection) { device->clearSelection(m_selection); } else { QRect oldExtent = device->extent(); device->clear(); device->setDirty(oldExtent); } runAndSaveCommand(KUndo2CommandSP(transaction.endAndTake()), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } void TransformStrokeStrategy::transformAndMergeDevice(const ToolTransformArgs &config, KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper) { KoUpdaterPtr mergeUpdater = src != dst ? helper->updater() : 0; KisTransformUtils::transformDevice(config, src, helper); if (src != dst) { QRect mergeRect = src->extent(); KisPainter painter(dst); painter.setProgress(mergeUpdater); painter.bitBlt(mergeRect.topLeft(), src, mergeRect); painter.end(); } } struct TransformExtraData : public KUndo2CommandExtraData { ToolTransformArgs savedTransformArgs; KisNodeSP rootNode; KisNodeList transformedNodes; KUndo2CommandExtraData* clone() const override { return new TransformExtraData(*this); } }; void TransformStrokeStrategy::postProcessToplevelCommand(KUndo2Command *command) { KIS_SAFE_ASSERT_RECOVER_RETURN(m_savedTransformArgs); TransformExtraData *data = new TransformExtraData(); data->savedTransformArgs = *m_savedTransformArgs; data->rootNode = m_rootNode; data->transformedNodes = m_processedNodes; command->setExtraData(data); KisSavedMacroCommand *macroCommand = dynamic_cast(command); KIS_SAFE_ASSERT_RECOVER_NOOP(macroCommand); if (m_overriddenCommand && macroCommand) { macroCommand->setOverrideInfo(m_overriddenCommand, m_skippedWhileMergeCommands); } KisStrokeStrategyUndoCommandBased::postProcessToplevelCommand(command); } bool TransformStrokeStrategy::fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode, KisNodeList *transformedNodes) { const TransformExtraData *data = dynamic_cast(command->extraData()); if (data) { *args = data->savedTransformArgs; *rootNode = data->rootNode; *transformedNodes = data->transformedNodes; } return bool(data); } QList TransformStrokeStrategy::fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive) { QList result; auto fetchFunc = [&result, mode, root] (KisNodeSP node) { if (node->isEditable(node == root) && (!node->inherits("KisShapeLayer") || mode == ToolTransformArgs::FREE_TRANSFORM) && !node->inherits("KisFileLayer") && (!node->inherits("KisTransformMask") || node == root)) { result << node; } }; if (recursive) { KisLayerUtils::recursiveApplyNodes(root, fetchFunc); } else { fetchFunc(root); } return result; } bool TransformStrokeStrategy::tryInitArgsFromNode(KisNodeSP node, ToolTransformArgs *args) { bool result = false; if (KisTransformMaskSP mask = dynamic_cast(node.data())) { KisTransformMaskParamsInterfaceSP savedParams = mask->transformParams(); KisTransformMaskAdapter *adapter = dynamic_cast(savedParams.data()); if (adapter) { *args = adapter->transformArgs(); result = true; } } return result; } bool TransformStrokeStrategy::tryFetchArgsFromCommandAndUndo(ToolTransformArgs *outArgs, ToolTransformArgs::TransformMode mode, KisNodeSP currentNode, KisNodeList selectedNodes, QVector *undoJobs) { bool result = false; const KUndo2Command *lastCommand = undoFacade()->lastExecutedCommand(); KisNodeSP oldRootNode; KisNodeList oldTransformedNodes; ToolTransformArgs args; if (lastCommand && TransformStrokeStrategy::fetchArgsFromCommand(lastCommand, &args, &oldRootNode, &oldTransformedNodes) && args.mode() == mode && oldRootNode == currentNode) { if (KritaUtils::compareListsUnordered(oldTransformedNodes, selectedNodes)) { args.saveContinuedState(); *outArgs = args; const KisSavedMacroCommand *command = dynamic_cast(lastCommand); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(command, false); // the jobs are fetched as !shouldGoToHistory, // so there is no need to put them into // m_skippedWhileMergeCommands command->getCommandExecutionJobs(undoJobs, true, false); m_overriddenCommand = command; result = true; } } return result; } void TransformStrokeStrategy::initStrokeCallback() { KisStrokeStrategyUndoCommandBased::initStrokeCallback(); if (m_selection) { m_selection->setVisible(false); m_deactivatedSelections.append(m_selection); } KisSelectionMaskSP overlaySelectionMask = dynamic_cast(m_rootNode->graphListener()->graphOverlayNode()); if (overlaySelectionMask) { - overlaySelectionMask->selection()->setVisible(false); - overlaySelectionMask->setDirty(); + overlaySelectionMask->setDecorationsVisible(false); m_deactivatedOverlaySelectionMask = overlaySelectionMask; } - ToolTransformArgs initialTransformArgs; m_processedNodes = fetchNodesList(m_mode, m_rootNode, m_workRecursively); bool argsAreInitialized = false; QVector lastCommandUndoJobs; if (!m_forceReset && tryFetchArgsFromCommandAndUndo(&initialTransformArgs, m_mode, m_rootNode, m_processedNodes, &lastCommandUndoJobs)) { argsAreInitialized = true; } else if (!m_forceReset && tryInitArgsFromNode(m_rootNode, &initialTransformArgs)) { argsAreInitialized = true; } QVector extraInitJobs; extraInitJobs << new Data(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::INITIALIZING), false, KisStrokeJobData::BARRIER); extraInitJobs << lastCommandUndoJobs; KritaUtils::addJobSequential(extraInitJobs, [this]() { /** * We must request shape layers to rerender areas outside image bounds */ KisLayerUtils::forceAllHiddenOriginalsUpdate(m_rootNode); }); KritaUtils::addJobBarrier(extraInitJobs, [this]() { /** * We must ensure that the currently selected subtree * has finished all its updates. */ KisLayerUtils::forceAllDelayedNodesUpdate(m_rootNode); }); + /// Disable all decorated nodes to generate outline + /// and preview correctly. We will enable them back + /// as soon as preview generation is finished. + KritaUtils::addJobBarrier(extraInitJobs, [this]() { + Q_FOREACH (KisNodeSP node, m_processedNodes) { + KisDecoratedNodeInterface *decoratedNode = dynamic_cast(node.data()); + if (decoratedNode && decoratedNode->decorationsVisible()) { + decoratedNode->setDecorationsVisible(false); + m_disabledDecoratedNodes << decoratedNode; + } + } + }); + KritaUtils::addJobBarrier(extraInitJobs, [this, initialTransformArgs, argsAreInitialized]() mutable { QRect srcRect; if (m_selection) { srcRect = m_selection->selectedExactRect(); } else { srcRect = QRect(); Q_FOREACH (KisNodeSP node, m_processedNodes) { // group layers may have a projection of layers // that are locked and will not be transformed if (node->inherits("KisGroupLayer")) continue; if (const KisTransformMask *mask = dynamic_cast(node.data())) { srcRect |= mask->sourceDataBounds(); } else if (const KisSelectionMask *mask = dynamic_cast(node.data())) { srcRect |= mask->selection()->selectedExactRect(); } else { srcRect |= node->exactBounds(); } } } TransformTransactionProperties transaction(srcRect, &initialTransformArgs, m_rootNode, m_processedNodes); if (!argsAreInitialized) { initialTransformArgs = KisTransformUtils::resetArgsForMode(m_mode, m_filterId, transaction); } this->m_initialTransformArgs = initialTransformArgs; emit this->sigTransactionGenerated(transaction, initialTransformArgs, this); }); extraInitJobs << new PreparePreviewData(); Q_FOREACH (KisNodeSP node, m_processedNodes) { extraInitJobs << new ClearSelectionData(node); } + /// recover back visibility of decorated nodes + KritaUtils::addJobBarrier(extraInitJobs, [this]() { + Q_FOREACH (KisDecoratedNodeInterface *decoratedNode, m_disabledDecoratedNodes) { + decoratedNode->setDecorationsVisible(true); + } + m_disabledDecoratedNodes.clear(); + }); + extraInitJobs << new Data(toQShared(new KisHoldUIUpdatesCommand(m_updatesFacade, KisCommandUtils::FlipFlopCommand::FINALIZING)), false, KisStrokeJobData::BARRIER); if (!lastCommandUndoJobs.isEmpty()) { KIS_SAFE_ASSERT_RECOVER_NOOP(m_overriddenCommand); for (auto it = extraInitJobs.begin(); it != extraInitJobs.end(); ++it) { (*it)->setCancellable(false); } } addMutatedJobs(extraInitJobs); } void TransformStrokeStrategy::finishStrokeImpl(bool applyTransform, const ToolTransformArgs &args) { /** * Since our finishStrokeCallback() initiates new jobs, * cancellation request may come even after * finishStrokeCallback() (cancellations may be called * until there are no jobs left in the stroke's queue). * * Therefore we should check for double-entry here and * make sure the finilizing jobs are no cancellable. */ if (m_finalizingActionsStarted) return; m_finalizingActionsStarted = true; QVector mutatedJobs; if (applyTransform) { Q_FOREACH (KisNodeSP node, m_processedNodes) { mutatedJobs << new TransformData(TransformData::PAINT_DEVICE, args, node); } mutatedJobs << new TransformData(TransformData::SELECTION, args, m_rootNode); } KritaUtils::addJobBarrier(mutatedJobs, [this, applyTransform]() { Q_FOREACH (KisSelectionSP selection, m_deactivatedSelections) { selection->setVisible(true); } Q_FOREACH (KisNodeSP node, m_hiddenProjectionLeaves) { node->projectionLeaf()->setTemporaryHiddenFromRendering(false); } if (m_deactivatedOverlaySelectionMask) { m_deactivatedOverlaySelectionMask->selection()->setVisible(true); m_deactivatedOverlaySelectionMask->setDirty(); } if (applyTransform) { KisStrokeStrategyUndoCommandBased::finishStrokeCallback(); } else { KisStrokeStrategyUndoCommandBased::cancelStrokeCallback(); } }); for (auto it = mutatedJobs.begin(); it != mutatedJobs.end(); ++it) { (*it)->setCancellable(false); } addMutatedJobs(mutatedJobs); } void TransformStrokeStrategy::finishStrokeCallback() { if (!m_savedTransformArgs || m_savedTransformArgs->isIdentity()) { cancelStrokeCallback(); return; } finishStrokeImpl(true, *m_savedTransformArgs); } void TransformStrokeStrategy::cancelStrokeCallback() { const bool shouldRecoverSavedInitialState = !m_initialTransformArgs.isIdentity(); if (shouldRecoverSavedInitialState) { m_savedTransformArgs = m_initialTransformArgs; } finishStrokeImpl(shouldRecoverSavedInitialState, *m_savedTransformArgs); } diff --git a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h index 6e1d6dc276..3baf28f9cd 100644 --- a/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h +++ b/plugins/tools/tool_transform2/strokes/transform_stroke_strategy.h @@ -1,178 +1,180 @@ /* * Copyright (c) 2013 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __TRANSFORM_STROKE_STRATEGY_H #define __TRANSFORM_STROKE_STRATEGY_H #include #include #include #include #include #include "tool_transform_args.h" #include #include #include class KisPostExecutionUndoAdapter; class TransformTransactionProperties; class KisUpdatesFacade; +class KisDecoratedNodeInterface; class TransformStrokeStrategy : public QObject, public KisStrokeStrategyUndoCommandBased { Q_OBJECT public: struct TransformAllData : public KisStrokeJobData { TransformAllData(const ToolTransformArgs &_config) : KisStrokeJobData(SEQUENTIAL, NORMAL), config(_config) {} ToolTransformArgs config; }; class TransformData : public KisStrokeJobData { public: enum Destination { PAINT_DEVICE, SELECTION, }; public: TransformData(Destination _destination, const ToolTransformArgs &_config, KisNodeSP _node) : KisStrokeJobData(CONCURRENT, NORMAL), destination(_destination), config(_config), node(_node) { } Destination destination; ToolTransformArgs config; KisNodeSP node; }; class ClearSelectionData : public KisStrokeJobData { public: ClearSelectionData(KisNodeSP _node) : KisStrokeJobData(SEQUENTIAL, NORMAL), node(_node) { } KisNodeSP node; }; class PreparePreviewData : public KisStrokeJobData { public: PreparePreviewData() : KisStrokeJobData(BARRIER, NORMAL) { } }; public: TransformStrokeStrategy(ToolTransformArgs::TransformMode mode, bool workRecursively, const QString &filterId, bool forceReset, KisNodeSP rootNode, KisSelectionSP selection, KisStrokeUndoFacade *undoFacade, KisUpdatesFacade *updatesFacade); ~TransformStrokeStrategy() override; void initStrokeCallback() override; void finishStrokeCallback() override; void cancelStrokeCallback() override; void doStrokeCallback(KisStrokeJobData *data) override; static bool fetchArgsFromCommand(const KUndo2Command *command, ToolTransformArgs *args, KisNodeSP *rootNode, KisNodeList *transformedNodes); Q_SIGNALS: void sigTransactionGenerated(TransformTransactionProperties transaction, ToolTransformArgs args, void *cookie); void sigPreviewDeviceReady(KisPaintDeviceSP device); protected: void postProcessToplevelCommand(KUndo2Command *command) override; private: KoUpdaterPtr fetchUpdater(KisNodeSP node); void transformAndMergeDevice(const ToolTransformArgs &config, KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper); void transformDevice(const ToolTransformArgs &config, KisPaintDeviceSP device, KisProcessingVisitor::ProgressHelper *helper); void clearSelection(KisPaintDeviceSP device); //void transformDevice(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisProcessingVisitor::ProgressHelper *helper); bool checkBelongsToSelection(KisPaintDeviceSP device) const; KisPaintDeviceSP createDeviceCache(KisPaintDeviceSP src); bool haveDeviceInCache(KisPaintDeviceSP src); void putDeviceCache(KisPaintDeviceSP src, KisPaintDeviceSP cache); KisPaintDeviceSP getDeviceCache(KisPaintDeviceSP src); QList fetchNodesList(ToolTransformArgs::TransformMode mode, KisNodeSP root, bool recursive); ToolTransformArgs resetArgsForMode(ToolTransformArgs::TransformMode mode, const QString &filterId, const TransformTransactionProperties &transaction); bool tryInitArgsFromNode(KisNodeSP node, ToolTransformArgs *args); bool tryFetchArgsFromCommandAndUndo(ToolTransformArgs *args, ToolTransformArgs::TransformMode mode, KisNodeSP currentNode, KisNodeList selectedNodes, QVector *undoJobs); void finishStrokeImpl(bool applyTransform, const ToolTransformArgs &args); private: KisUpdatesFacade *m_updatesFacade; ToolTransformArgs::TransformMode m_mode; bool m_workRecursively; QString m_filterId; bool m_forceReset; KisSelectionSP m_selection; QMutex m_devicesCacheMutex; QHash m_devicesCacheHash; KisTransformMaskSP writeToTransformMask; ToolTransformArgs m_initialTransformArgs; boost::optional m_savedTransformArgs; KisNodeSP m_rootNode; KisNodeList m_processedNodes; QList m_deactivatedSelections; QList m_hiddenProjectionLeaves; KisSelectionMaskSP m_deactivatedOverlaySelectionMask; + QVector m_disabledDecoratedNodes; const KisSavedMacroCommand *m_overriddenCommand = 0; QVector m_skippedWhileMergeCommands; bool m_finalizingActionsStarted = false; }; #endif /* __TRANSFORM_STROKE_STRATEGY_H */