diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp index 41d885145a..be3f1678e7 100644 --- a/libs/image/brushengine/kis_paintop_settings.cpp +++ b/libs/image/brushengine/kis_paintop_settings.cpp @@ -1,444 +1,450 @@ /* * Copyright (c) 2007 Boudewijn Rempt * Copyright (c) 2008 Lukáš Tvrdý * Copyright (c) 2014 Mohit Goyal * 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 #include #include #include #include #include #include #include #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_paintop_registry.h" #include #include "kis_paintop_config_widget.h" #include #include "kis_paintop_settings_update_proxy.h" #include #include #include #include #include struct Q_DECL_HIDDEN KisPaintOpSettings::Private { Private() : disableDirtyNotifications(false) {} QPointer settingsWidget; QString modelName; KisPaintOpPresetWSP preset; QList uniformProperties; bool disableDirtyNotifications; class DirtyNotificationsLocker { public: DirtyNotificationsLocker(KisPaintOpSettings::Private *d) : m_d(d), m_oldNotificationsState(d->disableDirtyNotifications) { m_d->disableDirtyNotifications = true; } ~DirtyNotificationsLocker() { m_d->disableDirtyNotifications = m_oldNotificationsState; } private: KisPaintOpSettings::Private *m_d; bool m_oldNotificationsState; Q_DISABLE_COPY(DirtyNotificationsLocker) }; KisPaintopSettingsUpdateProxy* updateProxyNoCreate() const { auto presetSP = preset.toStrongRef(); return presetSP ? presetSP->updateProxyNoCreate() : 0; } KisPaintopSettingsUpdateProxy* updateProxyCreate() const { auto presetSP = preset.toStrongRef(); return presetSP ? presetSP->updateProxy() : 0; } }; KisPaintOpSettings::KisPaintOpSettings() : d(new Private) { d->preset = 0; } KisPaintOpSettings::~KisPaintOpSettings() { } KisPaintOpSettings::KisPaintOpSettings(const KisPaintOpSettings &rhs) : KisPropertiesConfiguration(rhs) , d(new Private) { d->settingsWidget = 0; d->preset = rhs.preset(); d->modelName = rhs.modelName(); } void KisPaintOpSettings::setOptionsWidget(KisPaintOpConfigWidget* widget) { d->settingsWidget = widget; } void KisPaintOpSettings::setPreset(KisPaintOpPresetWSP preset) { d->preset = preset; } KisPaintOpPresetWSP KisPaintOpSettings::preset() const { return d->preset; } bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) { Q_UNUSED(modifiers); Q_UNUSED(currentNode); setRandomOffset(paintInformation); return true; // ignore the event by default } void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation) { if (getBool("Texture/Pattern/Enabled")) { if (getBool("Texture/Pattern/isRandomOffsetX")) { setProperty("Texture/Pattern/OffsetX", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetX"))); } if (getBool("Texture/Pattern/isRandomOffsetY")) { setProperty("Texture/Pattern/OffsetY", paintInformation.randomSource()->generate(0, KisPropertiesConfiguration::getInt("Texture/Pattern/MaximumOffsetY"))); } } } KisPaintOpSettingsSP KisPaintOpSettings::clone() const { QString paintopID = getString("paintop"); if (paintopID.isEmpty()) return 0; KisPaintOpSettingsSP settings = KisPaintOpRegistry::instance()->settings(KoID(paintopID, "")); QMapIterator i(getProperties()); while (i.hasNext()) { i.next(); settings->setProperty(i.key(), QVariant(i.value())); } settings->setPreset(this->preset()); return settings; } void KisPaintOpSettings::activate() { } void KisPaintOpSettings::setPaintOpOpacity(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("OpacityValue", value); } void KisPaintOpSettings::setPaintOpFlow(qreal value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("FlowValue", value); } void KisPaintOpSettings::setPaintOpCompositeOp(const QString &value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("CompositeOp", value); } qreal KisPaintOpSettings::paintOpOpacity() { KisLockedPropertiesProxySP proxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this); return proxy->getDouble("OpacityValue", 1.0); } qreal KisPaintOpSettings::paintOpFlow() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getDouble("FlowValue", 1.0); } QString KisPaintOpSettings::paintOpCompositeOp() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getString("CompositeOp", COMPOSITE_OVER); } void KisPaintOpSettings::setEraserMode(bool value) { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); proxy->setProperty("EraserMode", value); } bool KisPaintOpSettings::eraserMode() { KisLockedPropertiesProxySP proxy( KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(this)); return proxy->getBool("EraserMode", false); } QString KisPaintOpSettings::effectivePaintOpCompositeOp() { return !eraserMode() ? paintOpCompositeOp() : COMPOSITE_ERASE; } qreal KisPaintOpSettings::savedEraserSize() const { return getDouble("SavedEraserSize", 0.0); } void KisPaintOpSettings::setSavedEraserSize(qreal value) { setProperty("SavedEraserSize", value); setPropertyNotSaved("SavedEraserSize"); } qreal KisPaintOpSettings::savedBrushSize() const { return getDouble("SavedBrushSize", 0.0); } void KisPaintOpSettings::setSavedBrushSize(qreal value) { setProperty("SavedBrushSize", value); setPropertyNotSaved("SavedBrushSize"); } qreal KisPaintOpSettings::savedEraserOpacity() const { return getDouble("SavedEraserOpacity", 0.0); } void KisPaintOpSettings::setSavedEraserOpacity(qreal value) { setProperty("SavedEraserOpacity", value); setPropertyNotSaved("SavedEraserOpacity"); } qreal KisPaintOpSettings::savedBrushOpacity() const { return getDouble("SavedBrushOpacity", 0.0); } void KisPaintOpSettings::setSavedBrushOpacity(qreal value) { setProperty("SavedBrushOpacity", value); setPropertyNotSaved("SavedBrushOpacity"); } QString KisPaintOpSettings::modelName() const { return d->modelName; } void KisPaintOpSettings::setModelName(const QString & modelName) { d->modelName = modelName; } KisPaintOpConfigWidget* KisPaintOpSettings::optionsWidget() const { if (d->settingsWidget.isNull()) return 0; return d->settingsWidget.data(); } bool KisPaintOpSettings::isValid() const { return true; } bool KisPaintOpSettings::isLoadable() { return isValid(); } QString KisPaintOpSettings::indirectPaintingCompositeOp() const { return COMPOSITE_ALPHA_DARKEN; } bool KisPaintOpSettings::isAirbrushing() const { return getBool(AIRBRUSH_ENABLED, false); } qreal KisPaintOpSettings::airbrushInterval() const { - return 1000.0 / getDouble(AIRBRUSH_RATE, 0.0); + qreal rate = getDouble(AIRBRUSH_RATE, 1.0); + if (rate == 0.0) { + return 1000.0; + } + else { + return 1000.0 / rate; + } } QPainterPath KisPaintOpSettings::brushOutline(const KisPaintInformation &info, OutlineMode mode) { QPainterPath path; if (mode == CursorIsOutline || mode == CursorIsCircleOutline || mode == CursorTiltOutline) { path = ellipseOutline(10, 10, 1.0, 0); if (mode == CursorTiltOutline) { path.addPath(makeTiltIndicator(info, QPointF(0.0, 0.0), 0.0, 2.0)); } path.translate(info.pos()); } return path; } QPainterPath KisPaintOpSettings::ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation) { QPainterPath path; QRectF ellipse(0, 0, width * scale, height * scale); ellipse.translate(-ellipse.center()); path.addEllipse(ellipse); QTransform m; m.reset(); m.rotate(rotation); path = m.map(path); return path; } QPainterPath KisPaintOpSettings::makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal maxLength, qreal angle) { if (maxLength == 0.0) maxLength = 50.0; maxLength = qMax(maxLength, 50.0); qreal const length = maxLength * (1 - info.tiltElevation(info, 60.0, 60.0, true)); qreal const baseAngle = 360.0 - fmod(KisPaintInformation::tiltDirection(info, true) * 360.0 + 270.0, 360.0); QLineF guideLine = QLineF::fromPolar(length, baseAngle + angle); guideLine.translate(start); QPainterPath ret; ret.moveTo(guideLine.p1()); ret.lineTo(guideLine.p2()); guideLine.setAngle(baseAngle - angle); ret.lineTo(guideLine.p2()); ret.lineTo(guideLine.p1()); return ret; } void KisPaintOpSettings::setCanvasRotation(qreal angle) { Private::DirtyNotificationsLocker locker(d.data()); setProperty("runtimeCanvasRotation", angle); setPropertyNotSaved("runtimeCanvasRotation"); } void KisPaintOpSettings::setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored) { Private::DirtyNotificationsLocker locker(d.data()); setProperty("runtimeCanvasMirroredX", xAxisMirrored); setPropertyNotSaved("runtimeCanvasMirroredX"); setProperty("runtimeCanvasMirroredY", yAxisMirrored); setPropertyNotSaved("runtimeCanvasMirroredY"); } void KisPaintOpSettings::setProperty(const QString & name, const QVariant & value) { if (value != KisPropertiesConfiguration::getProperty(name) && !d->disableDirtyNotifications) { KisPaintOpPresetSP presetSP = preset().toStrongRef(); if (presetSP) { presetSP->setPresetDirty(true); } } KisPropertiesConfiguration::setProperty(name, value); onPropertyChanged(); } void KisPaintOpSettings::onPropertyChanged() { KisPaintopSettingsUpdateProxy *proxy = d->updateProxyNoCreate(); if (proxy) { proxy->notifySettingsChanged(); } } bool KisPaintOpSettings::isLodUserAllowed(const KisPropertiesConfigurationSP config) { return config->getBool("lodUserAllowed", true); } void KisPaintOpSettings::setLodUserAllowed(KisPropertiesConfigurationSP config, bool value) { config->setProperty("lodUserAllowed", value); } #include "kis_standard_uniform_properties_factory.h" QList KisPaintOpSettings::uniformProperties(KisPaintOpSettingsSP settings) { QList props = listWeakToStrong(d->uniformProperties); if (props.isEmpty()) { using namespace KisStandardUniformPropertiesFactory; props.append(createProperty(opacity, settings, d->updateProxyCreate())); props.append(createProperty(size, settings, d->updateProxyCreate())); props.append(createProperty(flow, settings, d->updateProxyCreate())); d->uniformProperties = listStrongToWeak(props); } return props; } diff --git a/libs/image/brushengine/kis_paintop_settings.h b/libs/image/brushengine/kis_paintop_settings.h index 12836412f5..1d9ac1a280 100644 --- a/libs/image/brushengine/kis_paintop_settings.h +++ b/libs/image/brushengine/kis_paintop_settings.h @@ -1,303 +1,303 @@ /* * 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_PAINTOP_SETTINGS_H_ #define KIS_PAINTOP_SETTINGS_H_ #include "kis_types.h" #include "kritaimage_export.h" #include #include #include "kis_properties_configuration.h" #include #include class KisPaintOpConfigWidget; class KisPaintopSettingsUpdateProxy; /** * Configuration property used to control whether airbrushing is enabled. */ const QString AIRBRUSH_ENABLED = "PaintOpSettings/isAirbrushing"; /** * Configuration property used to control airbrushing rate. The value should be in dabs per second. */ const QString AIRBRUSH_RATE = "PaintOpSettings/rate"; /** * Configuration property used to control whether airbrushing is configured to ignore distance-based * spacing. */ const QString AIRBRUSH_IGNORE_SPACING = "PaintOpSettings/ignoreSpacing"; /** * This class is used to cache the settings for a paintop * between two creations. There is one KisPaintOpSettings per input device (mouse, tablet, * etc...). * * The settings may be stored in a preset or a recorded brush stroke. Note that if your * paintop's settings subclass has data that is not stored as a property, that data is not * saved and restored. * * The object also contains a pointer to its parent KisPaintOpPreset object.This is to control the DirtyPreset * property of KisPaintOpPreset. Whenever the settings are changed/modified from the original -- the preset is * set to dirty. */ class KRITAIMAGE_EXPORT KisPaintOpSettings : public KisPropertiesConfiguration { public: KisPaintOpSettings(); ~KisPaintOpSettings() override; KisPaintOpSettings(const KisPaintOpSettings &rhs); /** * */ virtual void setOptionsWidget(KisPaintOpConfigWidget* widget); /** * This function is called by a tool when the mouse is pressed. It's useful if * the paintop needs mouse interaction for instance in the case of the clone op. * If the tool is supposed to ignore the event, the paint op should return false * and if the tool is supposed to use the event, return true. */ virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode); /** * Clone the current settings object. Override this if your settings instance doesn't * store everything as properties. */ virtual KisPaintOpSettingsSP clone() const; /** * @return the node the paintop is working on. */ KisNodeSP node() const; /** * Call this function when the paint op is selected or the tool is activated */ virtual void activate(); /** * XXX: Remove this after 2.0, when the paint operation (incremental/non incremental) will * be completely handled in the paintop, not in the tool. This is a filthy hack to move * the option to the right place, at least. * @return true if we paint incrementally, false if we paint like Photoshop. By default, paintops * do not support non-incremental. */ virtual bool paintIncremental() { return true; } /** * @return the composite op it to which the indirect painting device * should be initialized to. This is used by clone op to reset * the composite op to COMPOSITE_COPY */ virtual QString indirectPaintingCompositeOp() const; /** * Whether this paintop wants to deposit paint even when not moving, i.e. the tool needs to * activate its timer. If this is true, painting updates need to be generated at regular * intervals even in the absence of input device events, e.g. when the cursor is not moving. * * The default implementation checks the property AIRBRUSH_ENABLED, defaulting to false if the * property is not found. This should be suitable for most paintops. */ virtual bool isAirbrushing() const; /** * Indicates the minimum time interval that might be needed between airbrush dabs, in * milliseconds. A lower value means painting updates need to happen more frequently. This value * should be ignored if isAirbrushing() is false. * * The default implementation uses the property AIRBRUSH_RATE, defaulting to an interval of - * infinity if the property is not found. This should be suitable for most paintops. + * one second if the property is not found. This should be suitable for most paintops. */ virtual qreal airbrushInterval() const; /** * This enum defines the current mode for painting an outline. */ enum OutlineMode { CursorIsOutline = 1, ///< When this mode is set, an outline is painted around the cursor CursorIsCircleOutline, CursorNoOutline, CursorTiltOutline, CursorColorOutline }; /** * Returns the brush outline in pixel coordinates. Tool is responsible for conversion into view coordinates. * Outline mode has to be passed to the paintop which builds the outline as some paintops have to paint outline * always like clone paintop indicating the duplicate position */ virtual QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode); /** * Helpers for drawing the brush outline */ static QPainterPath ellipseOutline(qreal width, qreal height, qreal scale, qreal rotation); /** * Helper for drawing a triangle representing the tilt of the stylus. * * @param start is the offset from the brush's outline's bounding box * @param lengthScale is used for deciding the size of the triangle. * Brush diameter or width are common choices for this. * @param angle is the angle between the two sides of the triangle. */ static QPainterPath makeTiltIndicator(KisPaintInformation const& info, QPointF const& start, qreal lengthScale, qreal angle); /** * Set paintop opacity directly in the properties */ void setPaintOpOpacity(qreal value); /** * Set paintop flow directly in the properties */ void setPaintOpFlow(qreal value); /** * Set paintop composite mode directly in the properties */ void setPaintOpCompositeOp(const QString &value); /** * @return opacity saved in the properties */ qreal paintOpOpacity(); /** * @return flow saved in the properties */ qreal paintOpFlow(); /** * @return composite mode saved in the properties */ QString paintOpCompositeOp(); /** * Set paintop size directly in the properties */ virtual void setPaintOpSize(qreal value) = 0; /** * @return size saved in the properties */ virtual qreal paintOpSize() const = 0; void setEraserMode(bool value); bool eraserMode(); qreal savedEraserSize() const; void setSavedEraserSize(qreal value); qreal savedBrushSize() const; void setSavedBrushSize(qreal value); qreal savedEraserOpacity() const; void setSavedEraserOpacity(qreal value); qreal savedBrushOpacity() const; void setSavedBrushOpacity(qreal value); QString effectivePaintOpCompositeOp(); void setPreset(KisPaintOpPresetWSP preset); KisPaintOpPresetWSP preset() const; /** * @return filename of the 3D brush model, empty if no brush is set */ virtual QString modelName() const; /** * Set filename of 3D brush model. By default no brush is set */ void setModelName(const QString & modelName); /// Check if the settings are valid, setting might be invalid through missing brushes etc /// Overwrite if the settings of a paintop can be invalid /// @return state of the settings, default implementation is true virtual bool isValid() const; /// Check if the settings are loadable, that might the case if we can fallback to something /// Overwrite if the settings can do some kind of fallback /// @return loadable state of the settings, by default implementation return the same as isValid() virtual bool isLoadable(); /** * These methods are populating properties with runtime * information about canvas rotation/mirroring. This information * is set directly by KisToolFreehand. Later the data is accessed * by the pressure options to make a final decision. */ void setCanvasRotation(qreal angle); void setCanvasMirroring(bool xAxisMirrored, bool yAxisMirrored); /** * Overrides the method in KisPropertiesCofiguration to allow * onPropertyChanged() callback */ void setProperty(const QString & name, const QVariant & value) override; virtual QList uniformProperties(KisPaintOpSettingsSP settings); static bool isLodUserAllowed(const KisPropertiesConfigurationSP config); static void setLodUserAllowed(KisPropertiesConfigurationSP config, bool value); /** * @return the option widget of the paintop (can be 0 is no option widgets is set) */ KisPaintOpConfigWidget* optionsWidget() const; /** * This function is called to set random offsets to the brush whenever the mouse is clicked. It is * specific to when the pattern option is set. * */ virtual void setRandomOffset(const KisPaintInformation &paintInformation); protected: /** * The callback is called every time when a property changes */ virtual void onPropertyChanged(); private: struct Private; const QScopedPointer d; }; #endif diff --git a/libs/image/kis_distance_information.h b/libs/image/kis_distance_information.h index b28b101f17..aac36eba34 100644 --- a/libs/image/kis_distance_information.h +++ b/libs/image/kis_distance_information.h @@ -1,236 +1,234 @@ /* * Copyright (c) 2010 Cyrille Berger * 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 _KIS_DISTANCE_INFORMATION_H_ #define _KIS_DISTANCE_INFORMATION_H_ -#include - #include #include #include "kritaimage_export.h" class KisPaintInformation; /** * This structure contains information about the desired spacing * requested by the paintAt call */ class KisSpacingInformation { public: explicit KisSpacingInformation() : m_distanceSpacingEnabled(true) , m_distanceSpacing(0.0, 0.0) , m_timedSpacingEnabled(false) , m_timedSpacingInterval(0.0) , m_rotation(0.0) , m_coordinateSystemFlipped(false) { } explicit KisSpacingInformation(qreal isotropicSpacing) : m_distanceSpacingEnabled(true) , m_distanceSpacing(isotropicSpacing, isotropicSpacing) , m_timedSpacingEnabled(false) , m_timedSpacingInterval(0.0) , m_rotation(0.0) , m_coordinateSystemFlipped(false) { } explicit KisSpacingInformation(const QPointF &anisotropicSpacing, qreal rotation, bool coordinateSystemFlipped) : m_distanceSpacingEnabled(true) , m_distanceSpacing(anisotropicSpacing) , m_timedSpacingEnabled(false) , m_timedSpacingInterval(0.0) , m_rotation(rotation) , m_coordinateSystemFlipped(coordinateSystemFlipped) { } explicit KisSpacingInformation(qreal isotropicSpacing, qreal timedSpacingInterval) : m_distanceSpacingEnabled(true) , m_distanceSpacing(isotropicSpacing, isotropicSpacing) , m_timedSpacingEnabled(true) , m_timedSpacingInterval(timedSpacingInterval) , m_rotation(0.0) , m_coordinateSystemFlipped(false) { } explicit KisSpacingInformation(const QPointF &anisotropicSpacing, qreal rotation, bool coordinateSystemFlipped, qreal timedSpacingInterval) : m_distanceSpacingEnabled(true) , m_distanceSpacing(anisotropicSpacing) , m_timedSpacingEnabled(true) , m_timedSpacingInterval(timedSpacingInterval) , m_rotation(rotation) , m_coordinateSystemFlipped(coordinateSystemFlipped) { } explicit KisSpacingInformation(bool distanceSpacingEnabled, qreal isotropicSpacing, bool timedSpacingEnabled, qreal timedSpacingInterval) : m_distanceSpacingEnabled(distanceSpacingEnabled) , m_distanceSpacing(isotropicSpacing, isotropicSpacing) , m_timedSpacingEnabled(timedSpacingEnabled) , m_timedSpacingInterval(timedSpacingInterval) , m_rotation(0.0) , m_coordinateSystemFlipped(false) { } explicit KisSpacingInformation(bool distanceSpacingEnabled, const QPointF &anisotropicSpacing, qreal rotation, bool coordinateSystemFlipped, bool timedSpacingEnabled, qreal timedSpacingInterval) : m_distanceSpacingEnabled(distanceSpacingEnabled) , m_distanceSpacing(anisotropicSpacing) , m_timedSpacingEnabled(timedSpacingEnabled) , m_timedSpacingInterval(timedSpacingInterval) , m_rotation(rotation) , m_coordinateSystemFlipped(coordinateSystemFlipped) { } /** * @return True if and only if distance-based spacing is enabled. */ inline bool isDistanceSpacingEnabled() const { return m_distanceSpacingEnabled; } inline QPointF distanceSpacing() const { return m_distanceSpacing; } /** * @return True if and only if time-based spacing is enabled. */ inline bool isTimedSpacingEnabled() const { return m_timedSpacingEnabled; } /** - * @return The desired maximum amount of time between dabs, in milliseconds. Returns infinity if - * time-based spacing is disabled. + * @return The desired maximum amount of time between dabs, in milliseconds. Returns a time of + * approximately 1 year if time-based spacing is disabled. */ inline qreal timedSpacingInterval() const { return isTimedSpacingEnabled() ? m_timedSpacingInterval : - std::numeric_limits::infinity(); + 32000000000.0; } inline bool isIsotropic() const { return m_distanceSpacing.x() == m_distanceSpacing.y(); } inline qreal scalarApprox() const { return isIsotropic() ? m_distanceSpacing.x() : QVector2D(m_distanceSpacing).length(); } inline qreal rotation() const { return m_rotation; } bool coordinateSystemFlipped() const { return m_coordinateSystemFlipped; } private: // Distance-based spacing bool m_distanceSpacingEnabled; QPointF m_distanceSpacing; // Time-based spacing (interval is in milliseconds) bool m_timedSpacingEnabled; qreal m_timedSpacingInterval; qreal m_rotation; bool m_coordinateSystemFlipped; }; /** * This structure is used as return value of paintLine to contain * information that is needed to be passed for the next call. */ class KRITAIMAGE_EXPORT KisDistanceInformation { public: KisDistanceInformation(); KisDistanceInformation(const QPointF &lastPosition, qreal lastTime); KisDistanceInformation(const KisDistanceInformation &rhs); KisDistanceInformation(const KisDistanceInformation &rhs, int levelOfDetail); KisDistanceInformation& operator=(const KisDistanceInformation &rhs); ~KisDistanceInformation(); const KisSpacingInformation& currentSpacing() const; bool hasLastDabInformation() const; QPointF lastPosition() const; qreal lastTime() const; qreal lastDrawingAngle() const; bool hasLastPaintInformation() const; const KisPaintInformation& lastPaintInformation() const; void registerPaintedDab(const KisPaintInformation &info, const KisSpacingInformation &spacing); qreal getNextPointPosition(const QPointF &start, const QPointF &end, qreal startTime, qreal endTime); /** * \return true if at least one dab has been painted with this * distance information */ bool isStarted() const; bool hasLockedDrawingAngle() const; qreal lockedDrawingAngle() const; void setLockedDrawingAngle(qreal angle); qreal scalarDistanceApprox() const; void overrideLastValues(const QPointF &lastPosition, qreal lastTime); private: qreal getNextPointPositionIsotropic(const QPointF &start, const QPointF &end); qreal getNextPointPositionAnisotropic(const QPointF &start, const QPointF &end); qreal getNextPointPositionTimed(qreal startTime, qreal endTime); void resetAccumulators(); private: struct Private; Private * const m_d; }; #endif diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp index a890ec9e8e..971b6d76d5 100644 --- a/libs/ui/tool/kis_resources_snapshot.cpp +++ b/libs/ui/tool/kis_resources_snapshot.cpp @@ -1,390 +1,389 @@ /* * Copyright (c) 2011 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_resources_snapshot.h" #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" #include "filter/kis_filter_configuration.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "recorder/kis_recorded_paint_action.h" #include "kis_selection.h" #include "kis_selection_mask.h" #include "kis_algebra_2d.h" struct KisResourcesSnapshot::Private { Private() : currentPattern(0) , currentGradient(0) , currentGenerator(0) , compositeOp(0) { } KisImageSP image; KisDefaultBoundsBaseSP bounds; KoColor currentFgColor; KoColor currentBgColor; KoPattern *currentPattern; KoAbstractGradient *currentGradient; KisPaintOpPresetSP currentPaintOpPreset; KisNodeSP currentNode; qreal currentExposure; KisFilterConfigurationSP currentGenerator; QPointF axesCenter; bool mirrorMaskHorizontal; bool mirrorMaskVertical; quint8 opacity; QString compositeOpId; const KoCompositeOp *compositeOp; KisPainter::StrokeStyle strokeStyle; KisPainter::FillStyle fillStyle; bool globalAlphaLock; qreal effectiveZoom; bool presetAllowsLod; KisSelectionSP selectionOverride; }; KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; m_d->currentFgColor = resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value(); m_d->currentBgColor = resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value(); m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value(); m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value(); /** * We should deep-copy the preset, so that long-runnign actions * will have correct brush parameters. Theoretically this cloniong * can be expensive, but according to measurements, it takes * something like 0.1 ms for an average preset. */ m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value()->clone(); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble(); m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool(); m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool(); qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble(); m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8); m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentEffectiveCompositeOp).toString(); setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool(); m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble(); m_d->presetAllowsLod = resourceManager->resource(KisCanvasResourceProvider::PresetAllowsLod).toBool(); } KisResourcesSnapshot::KisResourcesSnapshot(KisImageSP image, KisNodeSP currentNode, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ QPointF relativeAxesCenter(0.5, 0.5); if (m_d->image) { relativeAxesCenter = m_d->image->mirrorAxesCenter(); } m_d->axesCenter = KisAlgebra2D::relativeToAbsolute(relativeAxesCenter, m_d->bounds->bounds()); m_d->opacity = OPACITY_OPAQUE_U8; setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; } KisResourcesSnapshot::~KisResourcesSnapshot() { delete m_d; } void KisResourcesSnapshot::setupPainter(KisPainter* painter) { painter->setPaintColor(m_d->currentFgColor); painter->setBackgroundColor(m_d->currentBgColor); painter->setGenerator(m_d->currentGenerator); painter->setPattern(m_d->currentPattern); painter->setGradient(m_d->currentGradient); QBitArray lockflags = channelLockFlags(); if (lockflags.size() > 0) { painter->setChannelFlags(lockflags); } painter->setOpacity(m_d->opacity); painter->setCompositeOp(m_d->compositeOp); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); painter->setFillStyle(m_d->fillStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image); } void KisResourcesSnapshot::setupPaintAction(KisRecordedPaintAction *action) { action->setPaintOpPreset(m_d->currentPaintOpPreset); action->setPaintIncremental(!needsIndirectPainting()); action->setPaintColor(m_d->currentFgColor); action->setBackgroundColor(m_d->currentBgColor); action->setGenerator(m_d->currentGenerator); action->setGradient(m_d->currentGradient); action->setPattern(m_d->currentPattern); action->setOpacity(m_d->opacity / qreal(OPACITY_OPAQUE_U8)); action->setCompositeOp(m_d->compositeOp->id()); action->setStrokeStyle(m_d->strokeStyle); action->setFillStyle(m_d->fillStyle); } KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const { return m_d->image ? m_d->image->postExecutionUndoAdapter() : 0; } void KisResourcesSnapshot::setCurrentNode(KisNodeSP node) { m_d->currentNode = node; KisPaintDeviceSP device; if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) { m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId); if(!m_d->compositeOp) { m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER); } } } void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { m_d->strokeStyle = strokeStyle; } void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle) { m_d->fillStyle = fillStyle; } KisNodeSP KisResourcesSnapshot::currentNode() const { return m_d->currentNode; } KisImageSP KisResourcesSnapshot::image() const { return m_d->image; } bool KisResourcesSnapshot::needsIndirectPainting() const { return !m_d->currentPaintOpPreset->settings()->paintIncremental(); } QString KisResourcesSnapshot::indirectPaintingCompositeOp() const { return m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp(); } KisSelectionSP KisResourcesSnapshot::activeSelection() const { /** * It is possible to have/use the snapshot without the image. Such * usecase is present for example in the scratchpad. */ if (m_d->selectionOverride) { return m_d->selectionOverride; } KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0; KisLayerSP layer = qobject_cast(m_d->currentNode.data()); KisSelectionMaskSP mask; if((layer = qobject_cast(m_d->currentNode.data()))) { selection = layer->selection(); } else if ((mask = dynamic_cast(m_d->currentNode.data())) && mask->selection() == selection) { selection = 0; } return selection; } bool KisResourcesSnapshot::needsAirbrushing() const { - return m_d->currentPaintOpPreset->settings()->isAirbrushing() - && airbrushingInterval() < std::numeric_limits::infinity(); + return m_d->currentPaintOpPreset->settings()->isAirbrushing(); } qreal KisResourcesSnapshot::airbrushingInterval() const { return m_d->currentPaintOpPreset->settings()->airbrushInterval(); } void KisResourcesSnapshot::setOpacity(qreal opacity) { m_d->opacity = opacity * OPACITY_OPAQUE_U8; } quint8 KisResourcesSnapshot::opacity() const { return m_d->opacity; } const KoCompositeOp* KisResourcesSnapshot::compositeOp() const { return m_d->compositeOp; } QString KisResourcesSnapshot::compositeOpId() const { return m_d->compositeOpId; } KoPattern* KisResourcesSnapshot::currentPattern() const { return m_d->currentPattern; } KoColor KisResourcesSnapshot::currentFgColor() const { return m_d->currentFgColor; } KoColor KisResourcesSnapshot::currentBgColor() const { return m_d->currentBgColor; } KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const { return m_d->currentPaintOpPreset; } QBitArray KisResourcesSnapshot::channelLockFlags() const { QBitArray channelFlags; KisPaintLayer *paintLayer; if ((paintLayer = dynamic_cast(m_d->currentNode.data()))) { channelFlags = paintLayer->channelLockFlags(); if (m_d->globalAlphaLock) { if (channelFlags.isEmpty()) { channelFlags = paintLayer->colorSpace()->channelFlags(true, true); } channelFlags &= paintLayer->colorSpace()->channelFlags(true, false); } } return channelFlags; } qreal KisResourcesSnapshot::effectiveZoom() const { return m_d->effectiveZoom; } bool KisResourcesSnapshot::presetAllowsLod() const { return m_d->presetAllowsLod; } void KisResourcesSnapshot::setFGColorOverride(const KoColor &color) { m_d->currentFgColor = color; } void KisResourcesSnapshot::setBGColorOverride(const KoColor &color) { m_d->currentBgColor = color; } void KisResourcesSnapshot::setSelectionOverride(KisSelectionSP selection) { m_d->selectionOverride = selection; } void KisResourcesSnapshot::setBrush(const KisPaintOpPresetSP &brush) { m_d->currentPaintOpPreset = brush; } diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index 1715894710..927230c819 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,184 +1,183 @@ /* * Copyright (c) 2008 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_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include #include "kis_painter.h" #include #include "kis_paintop_utils.h" #include "kis_paintop_plugin_utils.h" #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings) { if (KisBrushOption::isTextBrush(settings.data())) { KisBrushOption brushOption; brushOption.readOptionSettingForceCopy(settings); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings) { return (settings && settings == m_settings ? m_brush : 0); } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()), m_settings(settings) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOption brushOption; brushOption.readOptionSettingForceCopy(settings); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height()); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, &spacingOption, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption *spacingOption, const KisPressureRateOption *rateOption, const KisPaintInformation &pi) const { bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing(); MirrorProperties prop = m_mirrorOption.apply(pi); const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror; // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(), isotropicSpacing, rotation, implicitFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device()), m_settings, spacingOption, rateOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const { return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, 1.0, true, isotropicSpacing, rotation, axesFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), - false, - std::numeric_limits::infinity(), + false, 1000.0, KisLodTransform::lodToScale(painter()->device())); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h index 7ae60ef4ed..fc247d1dcb 100644 --- a/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h +++ b/plugins/paintops/libpaintop/kis_paintop_plugin_utils.h @@ -1,87 +1,91 @@ /* * Copyright (c) 2014 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 __KIS_PAINTOP_PLUGIN_UTILS_H #define __KIS_PAINTOP_PLUGIN_UTILS_H #include "kis_paint_information.h" #include "kis_paintop_utils.h" #include "kis_paintop_settings.h" #include "kis_pressure_spacing_option.h" #include "kis_pressure_rate_option.h" namespace KisPaintOpPluginUtils { /** * Similar to KisPaintOpUtils::effectiveSpacing, but some of the required parameters are obtained * from the provided configuration options. This function assumes a common configuration where * curve-based spacing and rate options provide pressure sensitivity, and airbrush settings are * configured through a KisPropertiesConfiguration. This type of configuration is used by several * different paintops. * @param propsConfig - The properties configuration for the paintop. Can be null to use a default * configuration. * @param spacingOption - The pressure-curve spacing option. Can be null for paintops that don't * support pressure-based spacing. * @param rateOption - The pressure-curve airbrush rate option. Can be null for paintops that don't * support a pressure-based airbrush rate. */ KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, bool isotropicSpacing, qreal rotation, bool axesFlipped, qreal spacingVal, bool autoSpacingActive, qreal autoSpacingCoeff, qreal lodScale, const KisPropertiesConfigurationSP propsConfig, const KisPressureSpacingOption *spacingOption, const KisPressureRateOption *rateOption, const KisPaintInformation &pi) { // Extract required parameters. // Note: timedSpacingInterval might end up being infinity. That shouldn't cause any problems. bool distanceSpacingEnabled = true; bool timedSpacingEnabled = false; - qreal timedSpacingInterval = std::numeric_limits::infinity(); + qreal timedSpacingInterval = 0.0; if (propsConfig) { timedSpacingEnabled = propsConfig->getBool(AIRBRUSH_ENABLED, false); distanceSpacingEnabled = !(timedSpacingEnabled && propsConfig->getBool(AIRBRUSH_IGNORE_SPACING, false)); - timedSpacingInterval = 1000.0 / propsConfig->getDouble(AIRBRUSH_RATE, 0.0); + qreal timedSpacingRate = propsConfig->getDouble(AIRBRUSH_RATE, 1.0); + if (timedSpacingRate == 0.0) { + timedSpacingRate = 1.0; + } + timedSpacingInterval = 1000.0 / timedSpacingRate; } qreal extraScale = 1.0; if (spacingOption && spacingOption->isChecked()) { extraScale = spacingOption->apply(pi); } qreal rateExtraScale = 1.0; if (rateOption && rateOption->isChecked()) { rateExtraScale = rateOption->apply(pi); } return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, rateExtraScale, distanceSpacingEnabled, isotropicSpacing, rotation, axesFlipped, spacingVal, autoSpacingActive, autoSpacingCoeff, timedSpacingEnabled, timedSpacingInterval, lodScale); } } #endif /* __KIS_PAINTOP_PLUGIN_UTILS_H */ diff --git a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h index 9c96b0fcf5..28077d62f5 100644 --- a/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h +++ b/plugins/paintops/roundmarker/kis_roundmarkerop_settings.h @@ -1,57 +1,57 @@ /* * Copyright (c) 2016 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 __KIS_ROUNDMARKEROP_SETTINGS_H #define __KIS_ROUNDMARKEROP_SETTINGS_H #include #include #include class KisRoundMarkerOpSettings : public KisOutlineGenerationPolicy { public: KisRoundMarkerOpSettings(); ~KisRoundMarkerOpSettings() override; bool paintIncremental() override; qreal paintOpSize() const override; void setPaintOpSize(qreal value) override; bool isAirbrushing() const override { return false; } qreal airbrushInterval() const override { - return std::numeric_limits::infinity(); + return 1000.0; } QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) override; QList uniformProperties(KisPaintOpSettingsSP settings) override; private: struct Private; const QScopedPointer m_d; }; #endif /* __KIS_ROUNDMARKEROP_SETTINGS_H */