diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui
index 62ae044a0e..38ec8d069d 100644
--- a/krita/krita4.xmlgui
+++ b/krita/krita4.xmlgui
@@ -1,390 +1,390 @@
&View
&Canvas
&Snap To
&Image
&Rotate
&Layer
New
&Import/Export
Import
&Convert
&Select
&Group
&Transform
&Rotate
S&plit
S&plit Alpha
&Select
Filte&r
&Tools
-
+ Scripting
Recording
Macros
Setti&ngs
&Help
File
Brushes and Stuff
diff --git a/libs/image/brushengine/kis_paintop_settings.cpp b/libs/image/brushengine/kis_paintop_settings.cpp
index 776eacafab..ce465f7f8c 100644
--- a/libs/image/brushengine/kis_paintop_settings.cpp
+++ b/libs/image/brushengine/kis_paintop_settings.cpp
@@ -1,512 +1,524 @@
/*
* 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 "kis_timing_information.h"
#include
#include "kis_paintop_config_widget.h"
#include
#include "kis_paintop_settings_update_proxy.h"
#include
#include
#include
#include
#include
#include "KisPaintopSettingsIds.h"
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")));
}
}
}
bool KisPaintOpSettings::hasMaskingSettings() const
{
return getBool(KisPaintOpUtils::MaskingBrushEnabledTag, false);
}
KisPaintOpSettingsSP KisPaintOpSettings::createMaskingSettings() const
{
if (!hasMaskingSettings()) return KisPaintOpSettingsSP();
const KoID pixelBrushId(KisPaintOpUtils::MaskingBrushPaintOpId, QString());
KisPaintOpSettingsSP maskingSettings = KisPaintOpRegistry::instance()->settings(pixelBrushId);
this->getPrefixedProperties(KisPaintOpUtils::MaskingBrushPresetPrefix, maskingSettings);
const bool useMasterSize = this->getBool(KisPaintOpUtils::MaskingBrushUseMasterSizeTag, true);
if (useMasterSize) {
const qreal masterSizeCoeff = getDouble(KisPaintOpUtils::MaskingBrushMasterSizeCoeffTag, 1.0);
maskingSettings->setPaintOpSize(masterSizeCoeff * paintOpSize());
}
return maskingSettings;
}
QString KisPaintOpSettings::maskingBrushCompositeOp() const
{
return getString(KisPaintOpUtils::MaskingBrushCompositeOpTag, COMPOSITE_MULT);
}
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::resetSettings()
+void KisPaintOpSettings::resetSettings(const QStringList &preserveProperties)
{
- const QString paintopID = getString("paintop");
+ QStringList allKeys = preserveProperties;
+ allKeys << "paintop";
+
+ QHash preserved;
+ Q_FOREACH (const QString &key, allKeys) {
+ if (hasProperty(key)) {
+ preserved[key] = getProperty(key);
+ }
+ }
+
clearProperties();
- setProperty("paintop", paintopID);
+
+ for (auto it = preserved.constBegin(); it != preserved.constEnd(); ++it) {
+ setProperty(it.key(), it.value());
+ }
}
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
{
qreal rate = getDouble(AIRBRUSH_RATE, 1.0);
if (rate == 0.0) {
return LONG_TIME;
}
else {
return 1000.0 / rate;
}
}
bool KisPaintOpSettings::useSpacingUpdates() const
{
return getBool(SPACING_USE_UPDATES, false);
}
bool KisPaintOpSettings::needsAsynchronousUpdates() const
{
return false;
}
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);
}
bool KisPaintOpSettings::lodSizeThresholdSupported() const
{
return true;
}
qreal KisPaintOpSettings::lodSizeThreshold() const
{
return getDouble("lodSizeThreshold", 100.0);
}
void KisPaintOpSettings::setLodSizeThreshold(qreal value)
{
setProperty("lodSizeThreshold", 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 6afbe4bedb..6efd7fe4f8 100644
--- a/libs/image/brushengine/kis_paintop_settings.h
+++ b/libs/image/brushengine/kis_paintop_settings.h
@@ -1,352 +1,352 @@
/*
* 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";
/**
* Configuration property used to control whether the spacing settings can be updated between
* painted dabs.
*/
const QString SPACING_USE_UPDATES = "PaintOpSettings/updateSpacingBetweenDabs";
/**
* 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);
/**
*
*/
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;
/**
* Removes all the settings from the object while keeping the paintop id,
* which is loaded to the object by the factory
*/
- void resetSettings();
+ void resetSettings(const QStringList &preserveProperties = QStringList());
/**
* @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
* one second if the property is not found. This should be suitable for most paintops.
*/
virtual qreal airbrushInterval() const;
/**
* Indicates whether this configuration allows spacing information to be updated between painted
* dabs during a stroke.
*/
virtual bool useSpacingUpdates() const;
/**
* Indicates if the tool should call paintOp->doAsynchronousUpdate() inbetween
* paintAt() calls to do the asynchronous rendering
*/
virtual bool needsAsynchronousUpdates() 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);
virtual bool lodSizeThresholdSupported() const;
qreal lodSizeThreshold() const;
void setLodSizeThreshold(qreal 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);
/**
* @return true if this preset demands a secondary masked brush running
* alongside it
*/
bool hasMaskingSettings() const;
/**
* @return a newly created settings object representing a preset of the masking
* brush that should be run alongside the current brush
*/
KisPaintOpSettingsSP createMaskingSettings() const;
/**
* @return a composite op id of the masked brush rendering algorithm.
*
* Please take into account that the brush itself always paints in alpha-
* darken mode, but the final result is combined with this composite op.
*/
QString maskingBrushCompositeOp() const;
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/floodfill/kis_scanline_fill.cpp b/libs/image/floodfill/kis_scanline_fill.cpp
index 39f7249817..e524d0a3d4 100644
--- a/libs/image/floodfill/kis_scanline_fill.cpp
+++ b/libs/image/floodfill/kis_scanline_fill.cpp
@@ -1,717 +1,728 @@
/*
* 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.
*/
#include "kis_scanline_fill.h"
#include
#include
#include
#include
#include
#include "kis_image.h"
#include "kis_fill_interval_map.h"
#include "kis_pixel_selection.h"
#include "kis_random_accessor_ng.h"
#include "kis_fill_sanity_checks.h"
template
class CopyToSelection : public BaseClass
{
public:
typedef KisRandomConstAccessorSP SourceAccessorType;
SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) {
return device->createRandomConstAccessorNG(0, 0);
}
public:
void setDestinationSelection(KisPaintDeviceSP pixelSelection) {
m_pixelSelection = pixelSelection;
m_it = m_pixelSelection->createRandomAccessorNG(0,0);
}
ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) {
Q_UNUSED(dstPtr);
m_it->moveTo(x, y);
*m_it->rawData() = opacity;
}
private:
KisPaintDeviceSP m_pixelSelection;
KisRandomAccessorSP m_it;
};
template
class FillWithColor : public BaseClass
{
public:
typedef KisRandomAccessorSP SourceAccessorType;
SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) {
return device->createRandomAccessorNG(0, 0);
}
public:
FillWithColor() : m_pixelSize(0) {}
void setFillColor(const KoColor &sourceColor) {
m_sourceColor = sourceColor;
m_pixelSize = sourceColor.colorSpace()->pixelSize();
m_data = m_sourceColor.data();
}
ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) {
Q_UNUSED(x);
Q_UNUSED(y);
if (opacity == MAX_SELECTED) {
memcpy(dstPtr, m_data, m_pixelSize);
}
}
private:
KoColor m_sourceColor;
const quint8 *m_data;
int m_pixelSize;
};
template
class FillWithColorExternal : public BaseClass
{
public:
typedef KisRandomConstAccessorSP SourceAccessorType;
SourceAccessorType createSourceDeviceAccessor(KisPaintDeviceSP device) {
return device->createRandomConstAccessorNG(0, 0);
}
public:
void setDestinationDevice(KisPaintDeviceSP device) {
m_externalDevice = device;
m_it = m_externalDevice->createRandomAccessorNG(0,0);
}
void setFillColor(const KoColor &sourceColor) {
m_sourceColor = sourceColor;
m_pixelSize = sourceColor.colorSpace()->pixelSize();
m_data = m_sourceColor.data();
}
ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) {
Q_UNUSED(dstPtr);
m_it->moveTo(x, y);
if (opacity == MAX_SELECTED) {
memcpy(m_it->rawData(), m_data, m_pixelSize);
}
}
private:
KisPaintDeviceSP m_externalDevice;
KisRandomAccessorSP m_it;
KoColor m_sourceColor;
const quint8 *m_data;
int m_pixelSize;
};
class DifferencePolicySlow
{
public:
ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold) {
m_colorSpace = device->colorSpace();
m_srcPixel = srcPixel;
m_srcPixelPtr = m_srcPixel.data();
m_threshold = threshold;
}
ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) {
if (m_threshold == 1) {
- return memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize());
+ if (memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize()) == 0) {
+ return 0;
+ }
+ return quint8_MAX;
}
else {
return m_colorSpace->difference(m_srcPixelPtr, pixelPtr);
}
}
private:
const KoColorSpace *m_colorSpace;
KoColor m_srcPixel;
const quint8 *m_srcPixelPtr;
int m_threshold;
};
template
class DifferencePolicyOptimized
{
typedef SrcPixelType HashKeyType;
typedef QHash HashType;
public:
ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold) {
m_colorSpace = device->colorSpace();
m_srcPixel = srcPixel;
m_srcPixelPtr = m_srcPixel.data();
m_threshold = threshold;
}
ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) {
HashKeyType key = *reinterpret_cast(pixelPtr);
quint8 result;
typename HashType::iterator it = m_differences.find(key);
if (it != m_differences.end()) {
result = *it;
} else {
if (m_threshold == 1) {
- result = memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize());
+ if (memcmp(m_srcPixelPtr, pixelPtr, m_colorSpace->pixelSize()) == 0) {
+ result = 0;
+ }
+ else {
+ result = quint8_MAX;
+ }
}
else {
result = m_colorSpace->difference(m_srcPixelPtr, pixelPtr);
}
m_differences.insert(key, result);
}
return result;
}
private:
HashType m_differences;
const KoColorSpace *m_colorSpace;
KoColor m_srcPixel;
const quint8 *m_srcPixelPtr;
int m_threshold;
};
template class PixelFiller>
class SelectionPolicy : public PixelFiller
{
public:
typename PixelFiller::SourceAccessorType m_srcIt;
public:
SelectionPolicy(KisPaintDeviceSP device, const KoColor &srcPixel, int threshold)
: m_threshold(threshold)
{
this->initDifferences(device, srcPixel, threshold);
m_srcIt = this->createSourceDeviceAccessor(device);
}
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) {
quint8 diff = this->calculateDifference(pixelPtr);
if (!useSmoothSelection) {
return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED;
} else {
quint8 selectionValue = qMax(0, m_threshold - diff);
quint8 result = MIN_SELECTED;
if (selectionValue > 0) {
qreal selectionNorm = qreal(selectionValue) / m_threshold;
result = MAX_SELECTED * selectionNorm;
}
return result;
}
}
private:
int m_threshold;
};
class IsNonNullPolicySlow
{
public:
ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int /*threshold*/) {
Q_UNUSED(srcPixel);
m_pixelSize = device->pixelSize();
m_testPixel.resize(m_pixelSize);
}
ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) {
- return memcmp(m_testPixel.data(), pixelPtr, m_pixelSize);
+ if (memcmp(m_testPixel.data(), pixelPtr, m_pixelSize) == 0) {
+ return 0;
+ }
+ return quint8_MAX;
}
private:
int m_pixelSize;
QByteArray m_testPixel;
};
template
class IsNonNullPolicyOptimized
{
public:
ALWAYS_INLINE void initDifferences(KisPaintDeviceSP device, const KoColor &srcPixel, int /*threshold*/) {
Q_UNUSED(device);
Q_UNUSED(srcPixel);
}
ALWAYS_INLINE quint8 calculateDifference(quint8* pixelPtr) {
SrcPixelType *pixel = reinterpret_cast(pixelPtr);
return *pixel == 0;
}
};
class GroupSplitPolicy
{
public:
typedef KisRandomAccessorSP SourceAccessorType;
SourceAccessorType m_srcIt;
public:
GroupSplitPolicy(KisPaintDeviceSP scribbleDevice,
KisPaintDeviceSP groupMapDevice,
qint32 groupIndex,
quint8 referenceValue, int threshold)
: m_threshold(threshold),
m_groupIndex(groupIndex),
m_referenceValue(referenceValue)
{
KIS_SAFE_ASSERT_RECOVER_NOOP(m_groupIndex > 0);
m_srcIt = scribbleDevice->createRandomAccessorNG(0,0);
m_groupMapIt = groupMapDevice->createRandomAccessorNG(0,0);
}
ALWAYS_INLINE quint8 calculateOpacity(quint8* pixelPtr) {
// TODO: either threshold should always be null, or there should be a special
// case for *pixelPtr == 0, which is different from all the other groups,
// whatever the threshold is
int diff = qAbs(int(*pixelPtr) - m_referenceValue);
return diff <= m_threshold ? MAX_SELECTED : MIN_SELECTED;
}
ALWAYS_INLINE void fillPixel(quint8 *dstPtr, quint8 opacity, int x, int y) {
Q_UNUSED(opacity);
// erase the scribble
*dstPtr = 0;
// write group index into the map
m_groupMapIt->moveTo(x, y);
qint32 *groupMapPtr = reinterpret_cast(m_groupMapIt->rawData());
if (*groupMapPtr != 0) {
qDebug() << ppVar(*groupMapPtr) << ppVar(m_groupIndex);
}
KIS_SAFE_ASSERT_RECOVER_NOOP(*groupMapPtr == 0);
*groupMapPtr = m_groupIndex;
}
private:
int m_threshold;
qint32 m_groupIndex;
quint8 m_referenceValue;
KisRandomAccessorSP m_groupMapIt;
};
struct Q_DECL_HIDDEN KisScanlineFill::Private
{
KisPaintDeviceSP device;
KisRandomAccessorSP it;
QPoint startPoint;
QRect boundingRect;
int threshold;
int rowIncrement;
KisFillIntervalMap backwardMap;
QStack forwardStack;
inline void swapDirection() {
rowIncrement *= -1;
SANITY_ASSERT_MSG(forwardStack.isEmpty(),
"FATAL: the forward stack must be empty "
"on a direction swap");
forwardStack = QStack(backwardMap.fetchAllIntervals(rowIncrement));
backwardMap.clear();
}
};
KisScanlineFill::KisScanlineFill(KisPaintDeviceSP device, const QPoint &startPoint, const QRect &boundingRect)
: m_d(new Private)
{
m_d->device = device;
m_d->it = device->createRandomAccessorNG(startPoint.x(), startPoint.y());
m_d->startPoint = startPoint;
m_d->boundingRect = boundingRect;
m_d->rowIncrement = 1;
m_d->threshold = 0;
}
KisScanlineFill::~KisScanlineFill()
{
}
void KisScanlineFill::setThreshold(int threshold)
{
m_d->threshold = threshold;
}
template
void KisScanlineFill::extendedPass(KisFillInterval *currentInterval, int srcRow, bool extendRight, T &pixelPolicy)
{
int x;
int endX;
int columnIncrement;
int *intervalBorder;
int *backwardIntervalBorder;
KisFillInterval backwardInterval(currentInterval->start, currentInterval->end, srcRow);
if (extendRight) {
x = currentInterval->end;
endX = m_d->boundingRect.right();
if (x >= endX) return;
columnIncrement = 1;
intervalBorder = ¤tInterval->end;
backwardInterval.start = currentInterval->end + 1;
backwardIntervalBorder = &backwardInterval.end;
} else {
x = currentInterval->start;
endX = m_d->boundingRect.left();
if (x <= endX) return;
columnIncrement = -1;
intervalBorder = ¤tInterval->start;
backwardInterval.end = currentInterval->start - 1;
backwardIntervalBorder = &backwardInterval.start;
}
do {
x += columnIncrement;
pixelPolicy.m_srcIt->moveTo(x, srcRow);
quint8 *pixelPtr = const_cast(pixelPolicy.m_srcIt->rawDataConst()); // TODO: avoid doing const_cast
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr);
if (opacity) {
*intervalBorder = x;
*backwardIntervalBorder = x;
pixelPolicy.fillPixel(pixelPtr, opacity, x, srcRow);
} else {
break;
}
} while (x != endX);
if (backwardInterval.isValid()) {
m_d->backwardMap.insertInterval(backwardInterval);
}
}
template
void KisScanlineFill::processLine(KisFillInterval interval, const int rowIncrement, T &pixelPolicy)
{
m_d->backwardMap.cropInterval(&interval);
if (!interval.isValid()) return;
int firstX = interval.start;
int lastX = interval.end;
int x = firstX;
int row = interval.row;
int nextRow = row + rowIncrement;
KisFillInterval currentForwardInterval;
int numPixelsLeft = 0;
quint8 *dataPtr = 0;
const int pixelSize = m_d->device->pixelSize();
while(x <= lastX) {
// a bit of optimzation for not calling slow random accessor
// methods too often
if (numPixelsLeft <= 0) {
pixelPolicy.m_srcIt->moveTo(x, row);
numPixelsLeft = pixelPolicy.m_srcIt->numContiguousColumns(x) - 1;
dataPtr = const_cast(pixelPolicy.m_srcIt->rawDataConst());
} else {
numPixelsLeft--;
dataPtr += pixelSize;
}
quint8 *pixelPtr = dataPtr;
quint8 opacity = pixelPolicy.calculateOpacity(pixelPtr);
if (opacity) {
if (!currentForwardInterval.isValid()) {
currentForwardInterval.start = x;
currentForwardInterval.end = x;
currentForwardInterval.row = nextRow;
} else {
currentForwardInterval.end = x;
}
pixelPolicy.fillPixel(pixelPtr, opacity, x, row);
if (x == firstX) {
extendedPass(¤tForwardInterval, row, false, pixelPolicy);
}
if (x == lastX) {
extendedPass(¤tForwardInterval, row, true, pixelPolicy);
}
} else {
if (currentForwardInterval.isValid()) {
m_d->forwardStack.push(currentForwardInterval);
currentForwardInterval.invalidate();
}
}
x++;
}
if (currentForwardInterval.isValid()) {
m_d->forwardStack.push(currentForwardInterval);
}
}
template
void KisScanlineFill::runImpl(T &pixelPolicy)
{
KIS_ASSERT_RECOVER_RETURN(m_d->forwardStack.isEmpty());
KisFillInterval startInterval(m_d->startPoint.x(), m_d->startPoint.x(), m_d->startPoint.y());
m_d->forwardStack.push(startInterval);
/**
* In the end of the first pass we should add an interval
* containing the starting pixel, but directed into the opposite
* direction. We cannot do it in the very beginning because the
* intervals are offset by 1 pixel during every swap operation.
*/
bool firstPass = true;
while (!m_d->forwardStack.isEmpty()) {
while (!m_d->forwardStack.isEmpty()) {
KisFillInterval interval = m_d->forwardStack.pop();
if (interval.row > m_d->boundingRect.bottom() ||
interval.row < m_d->boundingRect.top()) {
continue;
}
processLine(interval, m_d->rowIncrement, pixelPolicy);
}
m_d->swapDirection();
if (firstPass) {
startInterval.row--;
m_d->forwardStack.push(startInterval);
firstPass = false;
}
}
}
void KisScanlineFill::fillColor(const KoColor &fillColor)
{
KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y());
KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace());
const int pixelSize = m_d->device->pixelSize();
if (pixelSize == 1) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 2) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 4) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 8) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
runImpl(policy);
} else {
SelectionPolicy
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
runImpl(policy);
}
}
void KisScanlineFill::fillColor(const KoColor &fillColor, KisPaintDeviceSP externalDevice)
{
KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y());
KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace());
const int pixelSize = m_d->device->pixelSize();
if (pixelSize == 1) {
SelectionPolicy, FillWithColorExternal>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationDevice(externalDevice);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 2) {
SelectionPolicy, FillWithColorExternal>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationDevice(externalDevice);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 4) {
SelectionPolicy, FillWithColorExternal>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationDevice(externalDevice);
policy.setFillColor(fillColor);
runImpl(policy);
} else if (pixelSize == 8) {
SelectionPolicy, FillWithColorExternal>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationDevice(externalDevice);
policy.setFillColor(fillColor);
runImpl(policy);
} else {
SelectionPolicy
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationDevice(externalDevice);
policy.setFillColor(fillColor);
runImpl(policy);
}
}
void KisScanlineFill::fillSelection(KisPixelSelectionSP pixelSelection)
{
KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y());
KoColor srcColor(it->rawDataConst(), m_d->device->colorSpace());
const int pixelSize = m_d->device->pixelSize();
if (pixelSize == 1) {
SelectionPolicy, CopyToSelection>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 2) {
SelectionPolicy, CopyToSelection>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 4) {
SelectionPolicy, CopyToSelection>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else if (pixelSize == 8) {
SelectionPolicy, CopyToSelection>
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
} else {
SelectionPolicy
policy(m_d->device, srcColor, m_d->threshold);
policy.setDestinationSelection(pixelSelection);
runImpl(policy);
}
}
void KisScanlineFill::clearNonZeroComponent()
{
const int pixelSize = m_d->device->pixelSize();
KoColor srcColor(Qt::transparent, m_d->device->colorSpace());
if (pixelSize == 1) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(srcColor);
runImpl(policy);
} else if (pixelSize == 2) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(srcColor);
runImpl(policy);
} else if (pixelSize == 4) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(srcColor);
runImpl(policy);
} else if (pixelSize == 8) {
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(srcColor);
runImpl(policy);
} else {
SelectionPolicy
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(srcColor);
runImpl(policy);
}
}
void KisScanlineFill::fillContiguousGroup(KisPaintDeviceSP groupMapDevice, qint32 groupIndex)
{
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->device->pixelSize() == 1);
KIS_SAFE_ASSERT_RECOVER_RETURN(groupMapDevice->pixelSize() == 4);
KisRandomConstAccessorSP it = m_d->device->createRandomConstAccessorNG(m_d->startPoint.x(), m_d->startPoint.y());
const quint8 referenceValue = *it->rawDataConst();
GroupSplitPolicy policy(m_d->device, groupMapDevice, groupIndex, referenceValue, m_d->threshold);
runImpl(policy);
}
void KisScanlineFill::testingProcessLine(const KisFillInterval &processInterval)
{
KoColor srcColor(QColor(0,0,0,0), m_d->device->colorSpace());
KoColor fillColor(QColor(200,200,200,200), m_d->device->colorSpace());
SelectionPolicy, FillWithColor>
policy(m_d->device, srcColor, m_d->threshold);
policy.setFillColor(fillColor);
processLine(processInterval, 1, policy);
}
QVector KisScanlineFill::testingGetForwardIntervals() const
{
return QVector(m_d->forwardStack);
}
KisFillIntervalMap* KisScanlineFill::testingGetBackwardIntervals() const
{
return &m_d->backwardMap;
}
diff --git a/libs/image/kis_merge_walker.cc b/libs/image/kis_merge_walker.cc
index c0cff455d8..84df00b1c2 100644
--- a/libs/image/kis_merge_walker.cc
+++ b/libs/image/kis_merge_walker.cc
@@ -1,115 +1,121 @@
/*
* 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_merge_walker.h"
#include "kis_projection_leaf.h"
KisMergeWalker::KisMergeWalker(QRect cropRect, Flags flags)
: m_flags(flags)
{
setCropRect(cropRect);
}
KisMergeWalker::~KisMergeWalker()
{
}
KisBaseRectsWalker::UpdateType KisMergeWalker::type() const
{
return m_flags == DEFAULT ? KisBaseRectsWalker::UPDATE : KisBaseRectsWalker::UPDATE_NO_FILTHY;
}
-void KisMergeWalker::startTrip(KisProjectionLeafSP startLeaf)
+void KisMergeWalker::startTripImpl(KisProjectionLeafSP startLeaf, KisMergeWalker::Flags flags)
{
if(startLeaf->isMask()) {
- startTripWithMask(startLeaf);
+ startTripWithMask(startLeaf, flags);
return;
}
visitHigherNode(startLeaf,
- m_flags == DEFAULT ? N_FILTHY : N_ABOVE_FILTHY);
+ flags == DEFAULT ? N_FILTHY : N_ABOVE_FILTHY);
KisProjectionLeafSP prevLeaf = startLeaf->prevSibling();
if(prevLeaf)
visitLowerNode(prevLeaf);
}
-void KisMergeWalker::startTripWithMask(KisProjectionLeafSP filthyMask)
+
+void KisMergeWalker::startTrip(KisProjectionLeafSP startLeaf)
+{
+ startTripImpl(startLeaf, m_flags);
+}
+
+void KisMergeWalker::startTripWithMask(KisProjectionLeafSP filthyMask, KisMergeWalker::Flags flags)
{
/**
* Under very rare circumstances it may happen that the update
* queue will contain a job pointing to a node that has
* already been deleted from the image (direclty or by undo
* command). If it happens to a layer then the walker will
* handle it as usual by building a trivial graph pointing to
* nowhere, but when it happens to a mask... not. Because the
* mask is always expected to have a parent layer to process.
*
* So just handle it here separately.
*/
KisProjectionLeafSP parentLayer = filthyMask->parent();
if (!parentLayer) {
return;
}
adjustMasksChangeRect(filthyMask);
KisProjectionLeafSP nextLeaf = parentLayer->nextSibling();
KisProjectionLeafSP prevLeaf = parentLayer->prevSibling();
if (nextLeaf)
visitHigherNode(nextLeaf, N_ABOVE_FILTHY);
else if (parentLayer->parent())
- startTrip(parentLayer->parent());
+ startTripImpl(parentLayer->parent(), DEFAULT);
NodePosition positionToFilthy =
- (m_flags == DEFAULT ? N_FILTHY_PROJECTION : N_ABOVE_FILTHY) |
+ (flags == DEFAULT ? N_FILTHY_PROJECTION : N_ABOVE_FILTHY) |
calculateNodePosition(parentLayer);
registerNeedRect(parentLayer, positionToFilthy);
if(prevLeaf)
visitLowerNode(prevLeaf);
}
void KisMergeWalker::visitHigherNode(KisProjectionLeafSP leaf, NodePosition positionToFilthy)
{
positionToFilthy |= calculateNodePosition(leaf);
registerChangeRect(leaf, positionToFilthy);
KisProjectionLeafSP nextLeaf = leaf->nextSibling();
if (nextLeaf)
visitHigherNode(nextLeaf, N_ABOVE_FILTHY);
else if (leaf->parent())
- startTrip(leaf->parent());
+ startTripImpl(leaf->parent(), DEFAULT);
registerNeedRect(leaf, positionToFilthy);
}
void KisMergeWalker::visitLowerNode(KisProjectionLeafSP leaf)
{
NodePosition position =
N_BELOW_FILTHY | calculateNodePosition(leaf);
registerNeedRect(leaf, position);
KisProjectionLeafSP prevLeaf = leaf->prevSibling();
if (prevLeaf)
visitLowerNode(prevLeaf);
}
diff --git a/libs/image/kis_merge_walker.h b/libs/image/kis_merge_walker.h
index b582ca66a3..7a6cb842fc 100644
--- a/libs/image/kis_merge_walker.h
+++ b/libs/image/kis_merge_walker.h
@@ -1,89 +1,92 @@
/*
* 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.
*/
#ifndef __KIS_MERGE_WALKER_H
#define __KIS_MERGE_WALKER_H
#include "kis_types.h"
#include "kis_base_rects_walker.h"
class KisMergeWalker;
typedef KisSharedPtr KisMergeWalkerSP;
class KRITAIMAGE_EXPORT KisMergeWalker : public virtual KisBaseRectsWalker
{
public:
/**
* NO_FILTHY flag notifies the walker that there should be no (!)
* filthy node in the update. It means that the projection() of
* the node is already guaranteed to be ready, we just need to
* update all the higher-level nodes. Used by KisTransformMask
* regeneration code.
*/
enum Flags {
DEFAULT = 0,
NO_FILTHY
};
KisMergeWalker(QRect cropRect, Flags flags = DEFAULT);
~KisMergeWalker() override;
UpdateType type() const override;
protected:
KisMergeWalker() : m_flags(DEFAULT) {}
KisMergeWalker(Flags flags) : m_flags(flags) {}
/**
* Begins visiting nodes starting with @startWith.
* First it climbs to the top of the graph, collecting
* changeRects (it calls @registerChangeRect for every node).
* Then it goes down to the bottom collecting needRects
* for every branch.
*/
void startTrip(KisProjectionLeafSP startWith) override;
using KisBaseRectsWalker::startTrip;
- void startTripWithMask(KisProjectionLeafSP filthyMask);
+ void startTripWithMask(KisProjectionLeafSP filthyMask, KisMergeWalker::Flags flags);
+
+private:
+ void startTripImpl(KisProjectionLeafSP startLeaf, Flags flags);
private:
/**
* Visits a node @leaf and goes on crowling
* towards the top of the graph, caling visitHigherNode() or
* startTrip() one more time. After the top is reached
* returns back to the @leaf.
*/
void visitHigherNode(KisProjectionLeafSP leaf, NodePosition positionToFilthy);
/**
* Visits a node @leaf and goes on crowling
* towards the bottom of the graph, caling visitLowerNode() or
* startTrip() one more time.
*/
void visitLowerNode(KisProjectionLeafSP leaf);
private:
const Flags m_flags;
};
#endif /* __KIS_MERGE_WALKER_H */
diff --git a/libs/image/kis_paint_device.cc b/libs/image/kis_paint_device.cc
index ae852177b9..d8b1684e4e 100644
--- a/libs/image/kis_paint_device.cc
+++ b/libs/image/kis_paint_device.cc
@@ -1,2170 +1,2166 @@
/*
* Copyright (c) 2002 Patrick Julien
* Copyright (c) 2004 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_paint_device.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_image.h"
#include "kis_random_sub_accessor.h"
#include "kis_selection.h"
#include "kis_node.h"
#include "kis_datamanager.h"
#include "kis_paint_device_writer.h"
#include "kis_selection_component.h"
#include "kis_pixel_selection.h"
#include "kis_repeat_iterators_pixel.h"
#include "kis_fixed_paint_device.h"
#include "tiles3/kis_hline_iterator.h"
#include "tiles3/kis_vline_iterator.h"
#include "tiles3/kis_random_accessor.h"
#include "kis_default_bounds.h"
#include "kis_lod_transform.h"
#include "kis_raster_keyframe_channel.h"
#include "kis_paint_device_cache.h"
#include "kis_paint_device_data.h"
#include "kis_paint_device_frames_interface.h"
#include "kis_transform_worker.h"
#include "kis_filter_strategy.h"
#include "krita_utils.h"
struct KisPaintDeviceSPStaticRegistrar {
KisPaintDeviceSPStaticRegistrar() {
qRegisterMetaType("KisPaintDeviceSP");
}
};
static KisPaintDeviceSPStaticRegistrar __registrar;
struct KisPaintDevice::Private
{
/**
* Used when the paint device is loading to ensure no lod/animation
* interferes the process.
*/
static const KisDefaultBoundsSP transitionalDefaultBounds;
public:
class KisPaintDeviceStrategy;
class KisPaintDeviceWrappedStrategy;
Private(KisPaintDevice *paintDevice);
~Private();
KisPaintDevice *q;
KisNodeWSP parent;
QScopedPointer contentChannel;
KisDefaultBoundsBaseSP defaultBounds;
QScopedPointer basicStrategy;
QScopedPointer wrappedStrategy;
QMutex m_wrappedStrategyMutex;
QScopedPointer framesInterface;
bool isProjectionDevice;
KisPaintDeviceStrategy* currentStrategy();
void init(const KoColorSpace *cs, const quint8 *defaultPixel);
KUndo2Command* convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags);
bool assignProfile(const KoColorProfile * profile);
inline const KoColorSpace* colorSpace() const
{
return currentData()->colorSpace();
}
inline KisDataManagerSP dataManager() const
{
return currentData()->dataManager();
}
inline qint32 x() const
{
return currentData()->x();
}
inline qint32 y() const
{
return currentData()->y();
}
inline void setX(qint32 x)
{
currentData()->setX(x);
}
inline void setY(qint32 y)
{
currentData()->setY(y);
}
inline KisPaintDeviceCache* cache()
{
return currentData()->cache();
}
inline KisIteratorCompleteListener* cacheInvalidator() {
return currentData()->cacheInvalidator();
}
void cloneAllDataObjects(Private *rhs, bool copyFrames)
{
m_lodData.reset();
m_externalFrameData.reset();
if (!m_frames.isEmpty()) {
m_frames.clear();
}
if (!copyFrames) {
if (m_data) {
m_data->prepareClone(rhs->currentNonLodData(), true);
} else {
m_data = toQShared(new KisPaintDeviceData(rhs->currentNonLodData(), true));
}
} else {
if (m_data && !rhs->m_data) {
m_data.clear();
} else if (!m_data && rhs->m_data) {
m_data = toQShared(new KisPaintDeviceData(rhs->m_data.data(), true));
} else if (m_data && rhs->m_data) {
m_data->prepareClone(rhs->m_data.data(), true);
}
if (!rhs->m_frames.isEmpty()) {
FramesHash::const_iterator it = rhs->m_frames.constBegin();
FramesHash::const_iterator end = rhs->m_frames.constEnd();
for (; it != end; ++it) {
DataSP data = toQShared(new KisPaintDeviceData(it.value().data(), true));
m_frames.insert(it.key(), data);
}
}
m_nextFreeFrameId = rhs->m_nextFreeFrameId;
}
if (rhs->m_lodData) {
m_lodData.reset(new KisPaintDeviceData(rhs->m_lodData.data(), true));
}
}
void prepareClone(KisPaintDeviceSP src)
{
prepareCloneImpl(src, src->m_d->currentData());
Q_ASSERT(fastBitBltPossible(src));
}
bool fastBitBltPossible(KisPaintDeviceSP src)
{
return fastBitBltPossibleImpl(src->m_d->currentData());
}
int currentFrameId() const
{
KIS_ASSERT_RECOVER(contentChannel) {
return -1;
}
return !defaultBounds->currentLevelOfDetail() ?
contentChannel->frameIdAt(defaultBounds->currentTime()) :
-1;
}
KisDataManagerSP frameDataManager(int frameId) const
{
DataSP data = m_frames[frameId];
return data->dataManager();
}
void invalidateFrameCache(int frameId)
{
DataSP data = m_frames[frameId];
return data->cache()->invalidate();
}
private:
typedef KisPaintDeviceData Data;
typedef QSharedPointer DataSP;
typedef QHash FramesHash;
class FrameInsertionCommand : public KUndo2Command
{
public:
FrameInsertionCommand(FramesHash *hash, DataSP data, int frameId, bool insert, KUndo2Command *parentCommand)
: KUndo2Command(parentCommand),
m_hash(hash),
m_data(data),
m_frameId(frameId),
m_insert(insert)
{
}
void redo() override
{
doSwap(m_insert);
}
void undo() override
{
doSwap(!m_insert);
}
private:
void doSwap(bool insert)
{
if (insert) {
m_hash->insert(m_frameId, m_data);
} else {
DataSP deletedData = m_hash->take(m_frameId);
}
}
private:
FramesHash *m_hash;
DataSP m_data;
int m_frameId;
bool m_insert;
};
public:
int getNextFrameId() {
int frameId = 0;
while (m_frames.contains(frameId = m_nextFreeFrameId++));
KIS_SAFE_ASSERT_RECOVER_NOOP(!m_frames.contains(frameId));
return frameId;
}
int createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
{
KIS_ASSERT_RECOVER(parentCommand) {
return -1;
}
DataSP data;
bool initialFrame = false;
if (m_frames.isEmpty()) {
/**
* Here we move the contents of the paint device to the
* new frame and clear m_data to make the "background" for
* the areas where there is no frame at all.
*/
data = toQShared(new Data(m_data.data(), true));
m_data->dataManager()->clear();
m_data->cache()->invalidate();
initialFrame = true;
} else if (copy) {
DataSP srcData = m_frames[copySrc];
data = toQShared(new Data(srcData.data(), true));
} else {
DataSP srcData = m_frames.begin().value();
data = toQShared(new Data(srcData.data(), false));
}
if (!initialFrame && !copy) {
data->setX(offset.x());
data->setY(offset.y());
}
int frameId = getNextFrameId();
KUndo2Command *cmd =
new FrameInsertionCommand(&m_frames,
data,
frameId, true,
parentCommand);
cmd->redo();
return frameId;
}
void deleteFrame(int frame, KUndo2Command *parentCommand)
{
KIS_ASSERT_RECOVER_RETURN(m_frames.contains(frame));
KIS_ASSERT_RECOVER_RETURN(parentCommand);
DataSP deletedData = m_frames[frame];
KUndo2Command *cmd =
new FrameInsertionCommand(&m_frames,
deletedData,
frame, false,
parentCommand);
cmd->redo();
}
QRect frameBounds(int frameId)
{
DataSP data = m_frames[frameId];
QRect extent = data->dataManager()->extent();
extent.translate(data->x(), data->y());
return extent;
}
QPoint frameOffset(int frameId) const
{
DataSP data = m_frames[frameId];
return QPoint(data->x(), data->y());
}
void setFrameOffset(int frameId, const QPoint &offset)
{
DataSP data = m_frames[frameId];
data->setX(offset.x());
data->setY(offset.y());
}
const QList frameIds() const
{
return m_frames.keys();
}
bool readFrame(QIODevice *stream, int frameId)
{
bool retval = false;
DataSP data = m_frames[frameId];
retval = data->dataManager()->read(stream);
data->cache()->invalidate();
return retval;
}
bool writeFrame(KisPaintDeviceWriter &store, int frameId)
{
DataSP data = m_frames[frameId];
return data->dataManager()->write(store);
}
void setFrameDefaultPixel(const KoColor &defPixel, int frameId)
{
DataSP data = m_frames[frameId];
KoColor color(defPixel);
color.convertTo(data->colorSpace());
data->dataManager()->setDefaultPixel(color.data());
}
KoColor frameDefaultPixel(int frameId) const
{
DataSP data = m_frames[frameId];
return KoColor(data->dataManager()->defaultPixel(),
data->colorSpace());
}
void fetchFrame(int frameId, KisPaintDeviceSP targetDevice);
void uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice);
void uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice);
void uploadFrameData(DataSP srcData, DataSP dstData);
struct LodDataStructImpl;
LodDataStruct* createLodDataStruct(int lod);
void updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect);
void uploadLodDataStruct(LodDataStruct *dst);
QRegion regionForLodSyncing() const;
void tesingFetchLodDevice(KisPaintDeviceSP targetDevice);
private:
qint64 estimateDataSize(Data *data) const {
const QRect &rc = data->dataManager()->extent();
return rc.width() * rc.height() * data->colorSpace()->pixelSize();
}
public:
void estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const {
imageData = 0;
temporaryData = 0;
lodData = 0;
if (m_data) {
imageData += estimateDataSize(m_data.data());
-
- if (!m_frames.isEmpty()) {
- qWarning() << "WARNING: Extra data object:" << estimateDataSize(m_data.data());
- }
}
if (m_lodData) {
lodData += estimateDataSize(m_lodData.data());
}
if (m_externalFrameData) {
temporaryData += estimateDataSize(m_externalFrameData.data());
}
Q_FOREACH (DataSP value, m_frames.values()) {
imageData += estimateDataSize(value.data());
}
}
private:
QRegion syncWholeDevice(Data *srcData);
inline DataSP currentFrameData() const
{
DataSP data;
const int numberOfFrames = contentChannel->keyframeCount();
if (numberOfFrames > 1) {
int frameId = contentChannel->frameIdAt(defaultBounds->currentTime());
if (frameId == -1) {
data = m_data;
} else {
KIS_ASSERT_RECOVER(m_frames.contains(frameId)) {
return m_frames.begin().value();
}
data = m_frames[frameId];
}
} else if (numberOfFrames == 1) {
data = m_frames.begin().value();
} else {
data = m_data;
}
return data;
}
inline Data* currentNonLodData() const
{
Data *data = m_data.data();
if (contentChannel) {
data = currentFrameData().data();
} else if (isProjectionDevice && defaultBounds->externalFrameActive()) {
if (!m_externalFrameData) {
QMutexLocker l(&m_dataSwitchLock);
if (!m_externalFrameData) {
m_externalFrameData.reset(new Data(m_data.data(), false));
}
}
data = m_externalFrameData.data();
}
return data;
}
inline void ensureLodDataPresent() const
{
if (!m_lodData) {
Data *srcData = currentNonLodData();
QMutexLocker l(&m_dataSwitchLock);
if (!m_lodData) {
m_lodData.reset(new Data(srcData, false));
}
}
}
inline Data* currentData() const
{
Data *data;
if (defaultBounds->currentLevelOfDetail()) {
ensureLodDataPresent();
data = m_lodData.data();
} else {
data = currentNonLodData();
}
return data;
}
void prepareCloneImpl(KisPaintDeviceSP src, Data *srcData)
{
currentData()->prepareClone(srcData);
q->setDefaultPixel(KoColor(srcData->dataManager()->defaultPixel(), colorSpace()));
q->setDefaultBounds(src->defaultBounds());
}
bool fastBitBltPossibleImpl(Data *srcData)
{
return x() == srcData->x() && y() == srcData->y() &&
*colorSpace() == *srcData->colorSpace();
}
QList allDataObjects() const
{
QList dataObjects;
if (m_frames.isEmpty()) {
dataObjects << m_data.data();
}
dataObjects << m_lodData.data();
dataObjects << m_externalFrameData.data();
Q_FOREACH (DataSP value, m_frames.values()) {
dataObjects << value.data();
}
return dataObjects;
}
void transferFromData(Data *data, KisPaintDeviceSP targetDevice);
struct Q_DECL_HIDDEN StrategyPolicy;
typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialConstIterator;
typedef KisSequentialIteratorBase, StrategyPolicy> InternalSequentialIterator;
private:
friend class KisPaintDeviceFramesInterface;
private:
DataSP m_data;
mutable QScopedPointer m_lodData;
mutable QScopedPointer m_externalFrameData;
mutable QMutex m_dataSwitchLock;
FramesHash m_frames;
int m_nextFreeFrameId;
};
const KisDefaultBoundsSP KisPaintDevice::Private::transitionalDefaultBounds = new KisDefaultBounds();
#include "kis_paint_device_strategies.h"
KisPaintDevice::Private::Private(KisPaintDevice *paintDevice)
: q(paintDevice),
basicStrategy(new KisPaintDeviceStrategy(paintDevice, this)),
isProjectionDevice(false),
m_data(new Data(paintDevice)),
m_nextFreeFrameId(0)
{
}
KisPaintDevice::Private::~Private()
{
m_frames.clear();
}
KisPaintDevice::Private::KisPaintDeviceStrategy* KisPaintDevice::Private::currentStrategy()
{
if (!defaultBounds->wrapAroundMode()) {
return basicStrategy.data();
}
const QRect wrapRect = defaultBounds->bounds();
if (!wrappedStrategy || wrappedStrategy->wrapRect() != wrapRect) {
QMutexLocker locker(&m_wrappedStrategyMutex);
if (!wrappedStrategy) {
wrappedStrategy.reset(new KisPaintDeviceWrappedStrategy(wrapRect, q, this));
} else if (wrappedStrategy->wrapRect() != wrapRect) {
wrappedStrategy->setWrapRect(wrapRect);
}
}
return wrappedStrategy.data();
}
struct KisPaintDevice::Private::StrategyPolicy {
StrategyPolicy(KisPaintDevice::Private::KisPaintDeviceStrategy *strategy,
KisDataManager *dataManager, qint32 offsetX, qint32 offsetY)
: m_strategy(strategy),
m_dataManager(dataManager),
m_offsetX(offsetX),
m_offsetY(offsetY)
{
}
KisHLineConstIteratorSP createConstIterator(const QRect &rect)
{
return m_strategy->createHLineConstIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
}
KisHLineIteratorSP createIterator(const QRect &rect)
{
return m_strategy->createHLineIteratorNG(m_dataManager, rect.x(), rect.y(), rect.width(), m_offsetX, m_offsetY);
}
int pixelSize() const
{
return m_dataManager->pixelSize();
}
KisPaintDeviceStrategy *m_strategy;
KisDataManager *m_dataManager;
int m_offsetX;
int m_offsetY;
};
struct KisPaintDevice::Private::LodDataStructImpl : public KisPaintDevice::LodDataStruct {
LodDataStructImpl(Data *_lodData) : lodData(_lodData) {}
QScopedPointer lodData;
};
QRegion KisPaintDevice::Private::regionForLodSyncing() const
{
Data *srcData = currentNonLodData();
return srcData->dataManager()->region().translated(srcData->x(), srcData->y());
}
KisPaintDevice::LodDataStruct* KisPaintDevice::Private::createLodDataStruct(int newLod)
{
Data *srcData = currentNonLodData();
Data *lodData = new Data(srcData, false);
LodDataStruct *lodStruct = new LodDataStructImpl(lodData);
int expectedX = KisLodTransform::coordToLodCoord(srcData->x(), newLod);
int expectedY = KisLodTransform::coordToLodCoord(srcData->y(), newLod);
/**
* We compare color spaces as pure pointers, because they must be
* exactly the same, since they come from the common source.
*/
if (lodData->levelOfDetail() != newLod ||
lodData->colorSpace() != srcData->colorSpace() ||
lodData->x() != expectedX ||
lodData->y() != expectedY) {
lodData->prepareClone(srcData);
lodData->setLevelOfDetail(newLod);
lodData->setX(expectedX);
lodData->setY(expectedY);
// FIXME: different kind of synchronization
}
//QRegion dirtyRegion = syncWholeDevice(srcData);
lodData->cache()->invalidate();
return lodStruct;
}
void KisPaintDevice::Private::updateLodDataStruct(LodDataStruct *_dst, const QRect &originalRect)
{
LodDataStructImpl *dst = dynamic_cast(_dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
Data *lodData = dst->lodData.data();
Data *srcData = currentNonLodData();
const int lod = lodData->levelOfDetail();
const int srcStepSize = 1 << lod;
KIS_ASSERT_RECOVER_RETURN(lod > 0);
const QRect srcRect = KisLodTransform::alignedRect(originalRect, lod);
const QRect dstRect = KisLodTransform::scaledRect(srcRect, lod);
if (!srcRect.isValid() || !dstRect.isValid()) return;
KIS_ASSERT_RECOVER_NOOP(srcRect.width() / srcStepSize == dstRect.width());
const int pixelSize = srcData->dataManager()->pixelSize();
int rowsAccumulated = 0;
int columnsAccumulated = 0;
KoMixColorsOp *mixOp = colorSpace()->mixColorsOp();
QScopedArrayPointer blendData(new quint8[srcStepSize * srcRect.width() * pixelSize]);
quint8 *blendDataPtr = blendData.data();
int blendDataOffset = 0;
const int srcCellSize = srcStepSize * srcStepSize;
const int srcCellStride = srcCellSize * pixelSize;
const int srcStepStride = srcStepSize * pixelSize;
const int srcColumnStride = (srcStepSize - 1) * srcStepStride;
QScopedArrayPointer weights(new qint16[srcCellSize]);
{
const qint16 averageWeight = qCeil(255.0 / srcCellSize);
const qint16 extraWeight = averageWeight * srcCellSize - 255;
KIS_ASSERT_RECOVER_NOOP(extraWeight == 1);
for (int i = 0; i < srcCellSize - 1; i++) {
weights[i] = averageWeight;
}
weights[srcCellSize - 1] = averageWeight - extraWeight;
}
InternalSequentialConstIterator srcIntIt(StrategyPolicy(currentStrategy(), srcData->dataManager().data(), srcData->x(), srcData->y()), srcRect);
InternalSequentialIterator dstIntIt(StrategyPolicy(currentStrategy(), lodData->dataManager().data(), lodData->x(), lodData->y()), dstRect);
int rowsRemaining = srcRect.height();
while (rowsRemaining > 0) {
int colsRemaining = srcRect.width();
while (colsRemaining > 0 && srcIntIt.nextPixel()) {
memcpy(blendDataPtr, srcIntIt.rawDataConst(), pixelSize);
blendDataPtr += pixelSize;
columnsAccumulated++;
if (columnsAccumulated >= srcStepSize) {
blendDataPtr += srcColumnStride;
columnsAccumulated = 0;
}
colsRemaining--;
}
rowsAccumulated++;
if (rowsAccumulated >= srcStepSize) {
// blend and write the final data
blendDataPtr = blendData.data();
int colsRemaining = dstRect.width();
while (colsRemaining > 0 && dstIntIt.nextPixel()) {
mixOp->mixColors(blendDataPtr, weights.data(), srcCellSize, dstIntIt.rawData());
blendDataPtr += srcCellStride;
colsRemaining--;
}
// reset counters
rowsAccumulated = 0;
blendDataPtr = blendData.data();
blendDataOffset = 0;
} else {
blendDataOffset += srcStepStride;
blendDataPtr = blendData.data() + blendDataOffset;
}
rowsRemaining--;
}
}
void KisPaintDevice::Private::uploadLodDataStruct(LodDataStruct *_dst)
{
LodDataStructImpl *dst = dynamic_cast(_dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(dst);
KIS_SAFE_ASSERT_RECOVER_RETURN(
dst->lodData->levelOfDetail() == defaultBounds->currentLevelOfDetail());
ensureLodDataPresent();
m_lodData->prepareClone(dst->lodData.data());
m_lodData->dataManager()->bitBltRough(dst->lodData->dataManager(), dst->lodData->dataManager()->extent());
}
void KisPaintDevice::Private::transferFromData(Data *data, KisPaintDeviceSP targetDevice)
{
QRect extent = data->dataManager()->extent();
extent.translate(data->x(), data->y());
targetDevice->m_d->prepareCloneImpl(q, data);
targetDevice->m_d->currentStrategy()->fastBitBltRough(data->dataManager(), extent);
}
void KisPaintDevice::Private::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
{
DataSP data = m_frames[frameId];
transferFromData(data.data(), targetDevice);
}
void KisPaintDevice::Private::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
{
DataSP dstData = m_frames[dstFrameId];
KIS_ASSERT_RECOVER_RETURN(dstData);
DataSP srcData = srcDevice->m_d->m_frames[srcFrameId];
KIS_ASSERT_RECOVER_RETURN(srcData);
uploadFrameData(srcData, dstData);
}
void KisPaintDevice::Private::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
{
DataSP dstData = m_frames[dstFrameId];
KIS_ASSERT_RECOVER_RETURN(dstData);
DataSP srcData = srcDevice->m_d->m_data;
KIS_ASSERT_RECOVER_RETURN(srcData);
uploadFrameData(srcData, dstData);
}
void KisPaintDevice::Private::uploadFrameData(DataSP srcData, DataSP dstData)
{
if (srcData->colorSpace() != dstData->colorSpace() &&
*srcData->colorSpace() != *dstData->colorSpace()) {
KUndo2Command tempCommand;
srcData = toQShared(new Data(srcData.data(), true));
srcData->convertDataColorSpace(dstData->colorSpace(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags(),
&tempCommand);
}
dstData->dataManager()->clear();
dstData->cache()->invalidate();
const QRect rect = srcData->dataManager()->extent();
dstData->dataManager()->bitBltRough(srcData->dataManager(), rect);
dstData->setX(srcData->x());
dstData->setY(srcData->y());
}
void KisPaintDevice::Private::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
{
Data *data = m_lodData.data();
Q_ASSERT(data);
transferFromData(data, targetDevice);
}
KUndo2Command* KisPaintDevice::Private::convertColorSpace(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
class DeviceChangeColorSpaceCommand : public KUndo2Command
{
public:
DeviceChangeColorSpaceCommand(KisPaintDeviceSP device)
: m_firstRun(true),
m_device(device)
{
}
void emitNotifications()
{
m_device->emitColorSpaceChanged();
m_device->setDirty();
}
void redo() override
{
KUndo2Command::redo();
if (!m_firstRun) {
m_firstRun = false;
return;
}
emitNotifications();
}
void undo() override
{
KUndo2Command::undo();
emitNotifications();
}
private:
bool m_firstRun;
KisPaintDeviceSP m_device;
};
KUndo2Command *parentCommand = new DeviceChangeColorSpaceCommand(q);
QList dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
data->convertDataColorSpace(dstColorSpace, renderingIntent, conversionFlags, parentCommand);
}
if (!parentCommand->childCount()) {
delete parentCommand;
parentCommand = 0;
} else {
q->emitColorSpaceChanged();
}
return parentCommand;
}
bool KisPaintDevice::Private::assignProfile(const KoColorProfile * profile)
{
if (!profile) return false;
const KoColorSpace *dstColorSpace =
KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile);
if (!dstColorSpace) return false;
QList dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
data->assignColorSpace(dstColorSpace);
}
q->emitProfileChanged();
// no undo information is provided here
return true;
}
void KisPaintDevice::Private::init(const KoColorSpace *cs, const quint8 *defaultPixel)
{
QList dataObjects = allDataObjects();;
Q_FOREACH (Data *data, dataObjects) {
if (!data) continue;
KisDataManagerSP dataManager = new KisDataManager(cs->pixelSize(), defaultPixel);
data->init(cs, dataManager);
}
}
KisPaintDevice::KisPaintDevice(const KoColorSpace * colorSpace, const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(colorSpace, new KisDefaultBounds(), 0, name);
}
KisPaintDevice::KisPaintDevice(KisNodeWSP parent, const KoColorSpace * colorSpace, KisDefaultBoundsBaseSP defaultBounds, const QString& name)
: QObject(0)
, m_d(new Private(this))
{
init(colorSpace, defaultBounds, parent, name);
}
void KisPaintDevice::init(const KoColorSpace *colorSpace,
KisDefaultBoundsBaseSP defaultBounds,
KisNodeWSP parent, const QString& name)
{
Q_ASSERT(colorSpace);
setObjectName(name);
// temporary def. bounds object for the initialization phase only
m_d->defaultBounds = m_d->transitionalDefaultBounds;
if (!defaultBounds) {
// Reuse transitionalDefaultBounds here. Change if you change
// semantics of transitionalDefaultBounds
defaultBounds = m_d->transitionalDefaultBounds;
}
QScopedArrayPointer defaultPixel(new quint8[colorSpace->pixelSize()]);
colorSpace->fromQColor(Qt::transparent, defaultPixel.data());
m_d->init(colorSpace, defaultPixel.data());
Q_ASSERT(m_d->colorSpace());
setDefaultBounds(defaultBounds);
setParentNode(parent);
}
KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs, bool copyFrames, KisNode *newParentNode)
: QObject()
, KisShared()
, m_d(new Private(this))
{
if (this != &rhs) {
// temporary def. bounds object for the initialization phase only
m_d->defaultBounds = m_d->transitionalDefaultBounds;
// copy data objects with or without frames
m_d->cloneAllDataObjects(rhs.m_d, copyFrames);
if (copyFrames) {
KIS_ASSERT_RECOVER_RETURN(rhs.m_d->framesInterface);
KIS_ASSERT_RECOVER_RETURN(rhs.m_d->contentChannel);
m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
m_d->contentChannel.reset(new KisRasterKeyframeChannel(*rhs.m_d->contentChannel.data(), newParentNode, this));
}
setDefaultBounds(rhs.m_d->defaultBounds);
setParentNode(0);
}
}
KisPaintDevice::~KisPaintDevice()
{
delete m_d;
}
void KisPaintDevice::setProjectionDevice(bool value)
{
m_d->isProjectionDevice = value;
}
void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
{
m_d->prepareClone(src);
Q_ASSERT(fastBitBltPossible(src));
}
void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
{
prepareClone(src);
// we guarantee that *this is totally empty, so copy pixels that
// are areally present on the source image only
const QRect optimizedRect = rect & src->extent();
fastBitBlt(src, optimizedRect);
}
void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
{
prepareClone(src);
// we guarantee that *this is totally empty, so copy pixels that
// are areally present on the source image only
const QRect optimizedRect = minimalRect & src->extent();
fastBitBltRough(src, optimizedRect);
}
void KisPaintDevice::setDirty()
{
m_d->cache()->invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty();
}
void KisPaintDevice::setDirty(const QRect & rc)
{
m_d->cache()->invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(rc);
}
void KisPaintDevice::setDirty(const QRegion & region)
{
m_d->cache()->invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(region);
}
void KisPaintDevice::setDirty(const QVector rects)
{
m_d->cache()->invalidate();
if (m_d->parent.isValid())
m_d->parent->setDirty(rects);
}
void KisPaintDevice::requestTimeSwitch(int time)
{
if (m_d->parent.isValid()) {
m_d->parent->requestTimeSwitch(time);
}
}
int KisPaintDevice::sequenceNumber() const
{
return m_d->cache()->sequenceNumber();
}
void KisPaintDevice::estimateMemoryStats(qint64 &imageData, qint64 &temporaryData, qint64 &lodData) const
{
m_d->estimateMemoryStats(imageData, temporaryData, lodData);
}
void KisPaintDevice::setParentNode(KisNodeWSP parent)
{
m_d->parent = parent;
}
// for testing purposes only
KisNodeWSP KisPaintDevice::parentNode() const
{
return m_d->parent;
}
void KisPaintDevice::setDefaultBounds(KisDefaultBoundsBaseSP defaultBounds)
{
m_d->defaultBounds = defaultBounds;
m_d->cache()->invalidate();
}
KisDefaultBoundsBaseSP KisPaintDevice::defaultBounds() const
{
return m_d->defaultBounds;
}
void KisPaintDevice::moveTo(const QPoint &pt)
{
m_d->currentStrategy()->move(pt);
m_d->cache()->invalidate();
}
QPoint KisPaintDevice::offset() const
{
return QPoint(x(), y());
}
void KisPaintDevice::moveTo(qint32 x, qint32 y)
{
moveTo(QPoint(x, y));
}
void KisPaintDevice::setX(qint32 x)
{
moveTo(QPoint(x, m_d->y()));
}
void KisPaintDevice::setY(qint32 y)
{
moveTo(QPoint(m_d->x(), y));
}
qint32 KisPaintDevice::x() const
{
return m_d->x();
}
qint32 KisPaintDevice::y() const
{
return m_d->y();
}
void KisPaintDevice::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
{
QRect rc = extent();
x = rc.x();
y = rc.y();
w = rc.width();
h = rc.height();
}
QRect KisPaintDevice::extent() const
{
return m_d->currentStrategy()->extent();
}
QRegion KisPaintDevice::region() const
{
return m_d->currentStrategy()->region();
}
QRect KisPaintDevice::nonDefaultPixelArea() const
{
return m_d->cache()->nonDefaultPixelArea();
}
QRect KisPaintDevice::exactBounds() const
{
return m_d->cache()->exactBounds();
}
QRect KisPaintDevice::exactBoundsAmortized() const
{
return m_d->cache()->exactBoundsAmortized();
}
namespace Impl
{
struct CheckFullyTransparent {
CheckFullyTransparent(const KoColorSpace *colorSpace)
: m_colorSpace(colorSpace)
{
}
bool isPixelEmpty(const quint8 *pixelData)
{
return m_colorSpace->opacityU8(pixelData) == OPACITY_TRANSPARENT_U8;
}
private:
const KoColorSpace *m_colorSpace;
};
struct CheckNonDefault {
CheckNonDefault(int pixelSize, const quint8 *defaultPixel)
: m_pixelSize(pixelSize),
m_defaultPixel(defaultPixel)
{
}
bool isPixelEmpty(const quint8 *pixelData)
{
return memcmp(m_defaultPixel, pixelData, m_pixelSize) == 0;
}
private:
int m_pixelSize;
const quint8 *m_defaultPixel;
};
template
QRect calculateExactBoundsImpl(const KisPaintDevice *device, const QRect &startRect, const QRect &endRect, ComparePixelOp compareOp)
{
if (startRect == endRect) return startRect;
// Solution n°2
int x, y, w, h;
int boundLeft, boundTop, boundRight, boundBottom;
int endDirN, endDirE, endDirS, endDirW;
startRect.getRect(&x, &y, &w, &h);
if (endRect.isEmpty()) {
endDirS = startRect.bottom();
endDirN = startRect.top();
endDirE = startRect.right();
endDirW = startRect.left();
startRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
} else {
endDirS = endRect.top() - 1;
endDirN = endRect.bottom() + 1;
endDirE = endRect.left() - 1;
endDirW = endRect.right() + 1;
endRect.getCoords(&boundLeft, &boundTop, &boundRight, &boundBottom);
}
// XXX: a small optimization is possible by using H/V line iterators in the first
// and third cases, at the cost of making the code a bit more complex
KisRandomConstAccessorSP accessor = device->createRandomConstAccessorNG(x, y);
bool found = false;
{
for (qint32 y2 = y; y2 <= endDirS; ++y2) {
for (qint32 x2 = x; x2 < x + w || found; ++ x2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundTop = y2;
found = true;
break;
}
}
if (found) break;
}
}
/**
* If the first pass hasn't found any opaque pixel, there is no
* reason to check that 3 more times. They will not appear in the
* meantime. Just return an empty bounding rect.
*/
if (!found && endRect.isEmpty()) {
return QRect();
}
found = false;
for (qint32 y2 = y + h - 1; y2 >= endDirN ; --y2) {
for (qint32 x2 = x + w - 1; x2 >= x || found; --x2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundBottom = y2;
found = true;
break;
}
}
if (found) break;
}
found = false;
{
for (qint32 x2 = x; x2 <= endDirE ; ++x2) {
for (qint32 y2 = y; y2 < y + h || found; ++y2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundLeft = x2;
found = true;
break;
}
}
if (found) break;
}
}
found = false;
// Look for right edge )
{
for (qint32 x2 = x + w - 1; x2 >= endDirW; --x2) {
for (qint32 y2 = y + h - 1; y2 >= y || found; --y2) {
accessor->moveTo(x2, y2);
if (!compareOp.isPixelEmpty(accessor->rawDataConst())) {
boundRight = x2;
found = true;
break;
}
}
if (found) break;
}
}
return QRect(boundLeft, boundTop,
boundRight - boundLeft + 1,
boundBottom - boundTop + 1);
}
}
QRect KisPaintDevice::calculateExactBounds(bool nonDefaultOnly) const
{
QRect startRect = extent();
QRect endRect;
quint8 defaultOpacity = defaultPixel().opacityU8();
if (defaultOpacity != OPACITY_TRANSPARENT_U8) {
if (!nonDefaultOnly) {
/**
* We will calculate exact bounds only outside of the
* image bounds, and that'll be nondefault area only.
*/
endRect = defaultBounds()->bounds();
nonDefaultOnly = true;
} else {
startRect = region().boundingRect();
}
}
if (nonDefaultOnly) {
const KoColor defaultPixel = this->defaultPixel();
Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
} else {
Impl::CheckFullyTransparent compareOp(m_d->colorSpace());
endRect = Impl::calculateExactBoundsImpl(this, startRect, endRect, compareOp);
}
return endRect;
}
QRegion KisPaintDevice::regionExact() const
{
QRegion resultRegion;
QVector rects = region().rects();
const KoColor defaultPixel = this->defaultPixel();
Impl::CheckNonDefault compareOp(pixelSize(), defaultPixel.data());
Q_FOREACH (const QRect &rc1, rects) {
const int patchSize = 64;
QVector smallerRects = KritaUtils::splitRectIntoPatches(rc1, QSize(patchSize, patchSize));
Q_FOREACH (const QRect &rc2, smallerRects) {
const QRect result =
Impl::calculateExactBoundsImpl(this, rc2, QRect(), compareOp);
if (!result.isEmpty()) {
resultRegion += result;
}
}
}
return resultRegion;
}
void KisPaintDevice::crop(qint32 x, qint32 y, qint32 w, qint32 h)
{
crop(QRect(x, y, w, h));
}
void KisPaintDevice::crop(const QRect &rect)
{
m_d->currentStrategy()->crop(rect);
}
void KisPaintDevice::purgeDefaultPixels()
{
KisDataManagerSP dm = m_d->dataManager();
dm->purge(dm->extent());
}
void KisPaintDevice::setDefaultPixel(const KoColor &defPixel)
{
KoColor color(defPixel);
color.convertTo(colorSpace());
m_d->dataManager()->setDefaultPixel(color.data());
m_d->cache()->invalidate();
}
KoColor KisPaintDevice::defaultPixel() const
{
return KoColor(m_d->dataManager()->defaultPixel(), colorSpace());
}
void KisPaintDevice::clear()
{
m_d->dataManager()->clear();
m_d->cache()->invalidate();
}
void KisPaintDevice::clear(const QRect & rc)
{
m_d->currentStrategy()->clear(rc);
}
void KisPaintDevice::fill(const QRect & rc, const KoColor &color)
{
KIS_ASSERT_RECOVER_RETURN(*color.colorSpace() == *colorSpace());
m_d->currentStrategy()->fill(rc, color.data());
}
void KisPaintDevice::fill(qint32 x, qint32 y, qint32 w, qint32 h, const quint8 *fillPixel)
{
m_d->currentStrategy()->fill(QRect(x, y, w, h), fillPixel);
}
bool KisPaintDevice::write(KisPaintDeviceWriter &store)
{
return m_d->dataManager()->write(store);
}
bool KisPaintDevice::read(QIODevice *stream)
{
bool retval;
retval = m_d->dataManager()->read(stream);
m_d->cache()->invalidate();
return retval;
}
void KisPaintDevice::emitColorSpaceChanged()
{
emit colorSpaceChanged(m_d->colorSpace());
}
void KisPaintDevice::emitProfileChanged()
{
emit profileChanged(m_d->colorSpace()->profile());
}
KUndo2Command* KisPaintDevice::convertTo(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
KUndo2Command *command = m_d->convertColorSpace(dstColorSpace, renderingIntent, conversionFlags);
return command;
}
bool KisPaintDevice::setProfile(const KoColorProfile * profile)
{
return m_d->assignProfile(profile);
}
KisDataManagerSP KisPaintDevice::dataManager() const
{
return m_d->dataManager();
}
void KisPaintDevice::convertFromQImage(const QImage& _image, const KoColorProfile *profile,
qint32 offsetX, qint32 offsetY)
{
QImage image = _image;
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
// Don't convert if not no profile is given and both paint dev and qimage are rgba.
if (!profile && colorSpace()->id() == "RGBA") {
writeBytes(image.constBits(), offsetX, offsetY, image.width(), image.height());
} else {
try {
quint8 * dstData = new quint8[image.width() * image.height() * pixelSize()];
KoColorSpaceRegistry::instance()
->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), profile)
->convertPixelsTo(image.constBits(), dstData, colorSpace(), image.width() * image.height(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
writeBytes(dstData, offsetX, offsetY, image.width(), image.height());
delete[] dstData;
} catch (std::bad_alloc) {
warnKrita << "KisPaintDevice::convertFromQImage: Could not allocate" << image.width() * image.height() * pixelSize() << "bytes";
return;
}
}
m_d->cache()->invalidate();
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
qint32 x1;
qint32 y1;
qint32 w;
qint32 h;
QRect rc = exactBounds();
x1 = rc.x();
y1 = rc.y();
w = rc.width();
h = rc.height();
return convertToQImage(dstProfile, x1, y1, w, h, renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile,
const QRect &rc,
KoColorConversionTransformation::Intent renderingIntent,
KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
return convertToQImage(dstProfile,
rc.x(), rc.y(), rc.width(), rc.height(),
renderingIntent, conversionFlags);
}
QImage KisPaintDevice::convertToQImage(const KoColorProfile *dstProfile, qint32 x1, qint32 y1, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
{
if (w < 0)
return QImage();
if (h < 0)
return QImage();
quint8 *data = 0;
try {
data = new quint8 [w * h * pixelSize()];
} catch (std::bad_alloc) {
warnKrita << "KisPaintDevice::convertToQImage std::bad_alloc for " << w << " * " << h << " * " << pixelSize();
//delete[] data; // data is not allocated, so don't free it
return QImage();
}
Q_CHECK_PTR(data);
// XXX: Is this really faster than converting line by line and building the QImage directly?
// This copies potentially a lot of data.
readBytes(data, x1, y1, w, h);
QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, renderingIntent, conversionFlags);
delete[] data;
return image;
}
inline bool moveBy(KisSequentialConstIterator& iter, int numPixels)
{
int pos = 0;
while (pos < numPixels) {
int step = std::min(iter.nConseqPixels(), numPixels - pos);
if (!iter.nextPixels(step))
return false;
pos += step;
}
return true;
}
static KisPaintDeviceSP createThumbnailDeviceInternal(const KisPaintDevice* srcDev, qint32 srcX0, qint32 srcY0, qint32 srcWidth, qint32 srcHeight, qint32 w, qint32 h, QRect outputRect)
{
KisPaintDeviceSP thumbnail = new KisPaintDevice(srcDev->colorSpace());
qint32 pixelSize = srcDev->pixelSize();
KisRandomConstAccessorSP srcIter = srcDev->createRandomConstAccessorNG(0, 0);
KisRandomAccessorSP dstIter = thumbnail->createRandomAccessorNG(0, 0);
for (qint32 y = outputRect.y(); y < outputRect.y() + outputRect.height(); ++y) {
qint32 iY = srcY0 + (y * srcHeight) / h;
for (qint32 x = outputRect.x(); x < outputRect.x() + outputRect.width(); ++x) {
qint32 iX = srcX0 + (x * srcWidth) / w;
srcIter->moveTo(iX, iY);
dstIter->moveTo(x, y);
memcpy(dstIter->rawData(), srcIter->rawDataConst(), pixelSize);
}
}
return thumbnail;
}
QSize fixThumbnailSize(QSize size)
{
if (!size.width() && size.height()) {
size.setWidth(1);
}
if (size.width() && !size.height()) {
size.setHeight(1);
}
return size;
}
KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(qint32 w, qint32 h, QRect rect, QRect outputRect) const
{
QSize thumbnailSize(w, h);
QRect imageRect = rect.isValid() ? rect : extent();
if ((thumbnailSize.width() > imageRect.width()) || (thumbnailSize.height() > imageRect.height())) {
thumbnailSize.scale(imageRect.size(), Qt::KeepAspectRatio);
}
thumbnailSize = fixThumbnailSize(thumbnailSize);
//can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
if (imageRect.isEmpty() || thumbnailSize.isEmpty()) {
return new KisPaintDevice(colorSpace());
}
int srcWidth, srcHeight;
int srcX0, srcY0;
imageRect.getRect(&srcX0, &srcY0, &srcWidth, &srcHeight);
if (!outputRect.isValid()) {
outputRect = QRect(0, 0, w, h);
}
KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
thumbnailSize.width(), thumbnailSize.height(), outputRect);
return thumbnail;
}
KisPaintDeviceSP KisPaintDevice::createThumbnailDeviceOversampled(qint32 w, qint32 h, qreal oversample, QRect rect, QRect outputTileRect) const
{
QSize thumbnailSize(w, h);
qreal oversampleAdjusted = qMax(oversample, 1.);
QSize thumbnailOversampledSize = oversampleAdjusted * thumbnailSize;
QRect outputRect;
QRect imageRect = rect.isValid() ? rect : extent();
qint32 hstart = thumbnailOversampledSize.height();
if ((thumbnailOversampledSize.width() > imageRect.width()) || (thumbnailOversampledSize.height() > imageRect.height())) {
thumbnailOversampledSize.scale(imageRect.size(), Qt::KeepAspectRatio);
}
thumbnailOversampledSize = fixThumbnailSize(thumbnailOversampledSize);
//can't create thumbnail for an empty device, e.g. layer thumbnail for empty image
if (imageRect.isEmpty() || thumbnailSize.isEmpty() || thumbnailOversampledSize.isEmpty()) {
return new KisPaintDevice(colorSpace());
}
oversampleAdjusted *= (hstart > 0) ? ((qreal)thumbnailOversampledSize.height() / hstart) : 1.; //readjusting oversample ratio, given that we had to adjust thumbnail size
outputRect = QRect(0, 0, thumbnailOversampledSize.width(), thumbnailOversampledSize.height());
if (outputTileRect.isValid()) {
//compensating output rectangle for oversampling
outputTileRect = QRect(oversampleAdjusted * outputTileRect.topLeft(), oversampleAdjusted * outputTileRect.bottomRight());
outputRect = outputRect.intersected(outputTileRect);
}
KisPaintDeviceSP thumbnail = createThumbnailDeviceInternal(this, imageRect.x(), imageRect.y(), imageRect.width(), imageRect.height(),
thumbnailOversampledSize.width(), thumbnailOversampledSize.height(), outputRect);
if (oversample != 1. && oversampleAdjusted != 1.) {
KoDummyUpdater updater;
KisTransformWorker worker(thumbnail, 1 / oversampleAdjusted, 1 / oversampleAdjusted, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
&updater, KisFilterStrategyRegistry::instance()->value("Bilinear"));
worker.run();
}
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, QRect rect, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
QSize size = fixThumbnailSize(QSize(w, h));
KisPaintDeviceSP dev = createThumbnailDeviceOversampled(size.width(), size.height(), oversample, rect);
QImage thumbnail = dev->convertToQImage(KoColorSpaceRegistry::instance()->rgb8()->profile(), 0, 0, w, h, renderingIntent, conversionFlags);
return thumbnail;
}
QImage KisPaintDevice::createThumbnail(qint32 w, qint32 h, qreal oversample, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
{
QSize size = fixThumbnailSize(QSize(w, h));
return m_d->cache()->createThumbnail(size.width(), size.height(), oversample, renderingIntent, conversionFlags);
}
KisHLineIteratorSP KisPaintDevice::createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
{
m_d->cache()->invalidate();
return m_d->currentStrategy()->createHLineIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
}
KisHLineConstIteratorSP KisPaintDevice::createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
{
return m_d->currentStrategy()->createHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y());
}
KisVLineIteratorSP KisPaintDevice::createVLineIteratorNG(qint32 x, qint32 y, qint32 w)
{
m_d->cache()->invalidate();
return m_d->currentStrategy()->createVLineIteratorNG(x, y, w);
}
KisVLineConstIteratorSP KisPaintDevice::createVLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
{
return m_d->currentStrategy()->createVLineConstIteratorNG(x, y, w);
}
KisRepeatHLineConstIteratorSP KisPaintDevice::createRepeatHLineConstIterator(qint32 x, qint32 y, qint32 w, const QRect& _dataWidth) const
{
return new KisRepeatHLineConstIteratorNG(m_d->dataManager().data(), x, y, w, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
}
KisRepeatVLineConstIteratorSP KisPaintDevice::createRepeatVLineConstIterator(qint32 x, qint32 y, qint32 h, const QRect& _dataWidth) const
{
return new KisRepeatVLineConstIteratorNG(m_d->dataManager().data(), x, y, h, m_d->x(), m_d->y(), _dataWidth, m_d->cacheInvalidator());
}
KisRandomAccessorSP KisPaintDevice::createRandomAccessorNG(qint32 x, qint32 y)
{
m_d->cache()->invalidate();
return m_d->currentStrategy()->createRandomAccessorNG(x, y);
}
KisRandomConstAccessorSP KisPaintDevice::createRandomConstAccessorNG(qint32 x, qint32 y) const
{
return m_d->currentStrategy()->createRandomConstAccessorNG(x, y);
}
KisRandomSubAccessorSP KisPaintDevice::createRandomSubAccessor() const
{
KisPaintDevice* pd = const_cast(this);
return new KisRandomSubAccessor(pd);
}
void KisPaintDevice::clearSelection(KisSelectionSP selection)
{
const KoColorSpace *colorSpace = m_d->colorSpace();
QRect r = selection->selectedExactRect() & m_d->defaultBounds->bounds();
if (r.isValid()) {
KisHLineIteratorSP devIt = createHLineIteratorNG(r.x(), r.y(), r.width());
KisHLineConstIteratorSP selectionIt = selection->projection()->createHLineConstIteratorNG(r.x(), r.y(), r.width());
const KoColor defaultPixel = this->defaultPixel();
bool transparentDefault = (defaultPixel.opacityU8() == OPACITY_TRANSPARENT_U8);
for (qint32 y = 0; y < r.height(); y++) {
do {
// XXX: Optimize by using stretches
colorSpace->applyInverseAlphaU8Mask(devIt->rawData(), selectionIt->rawDataConst(), 1);
if (transparentDefault && colorSpace->opacityU8(devIt->rawData()) == OPACITY_TRANSPARENT_U8) {
memcpy(devIt->rawData(), defaultPixel.data(), colorSpace->pixelSize());
}
} while (devIt->nextPixel() && selectionIt->nextPixel());
devIt->nextRow();
selectionIt->nextRow();
}
m_d->dataManager()->purge(r.translated(-m_d->x(), -m_d->y()));
setDirty(r);
}
}
bool KisPaintDevice::pixel(qint32 x, qint32 y, QColor *c) const
{
KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->rawDataConst();
if (!pix) return false;
colorSpace()->toQColor(pix, c);
return true;
}
bool KisPaintDevice::pixel(qint32 x, qint32 y, KoColor * kc) const
{
KisHLineConstIteratorSP iter = createHLineConstIteratorNG(x, y, 1);
const quint8 *pix = iter->rawDataConst();
if (!pix) return false;
kc->setColor(pix, m_d->colorSpace());
return true;
}
bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c)
{
KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
colorSpace()->fromQColor(c, iter->rawData());
m_d->cache()->invalidate();
return true;
}
bool KisPaintDevice::setPixel(qint32 x, qint32 y, const KoColor& kc)
{
const quint8 * pix;
KisHLineIteratorSP iter = createHLineIteratorNG(x, y, 1);
if (kc.colorSpace() != m_d->colorSpace()) {
KoColor kc2(kc, m_d->colorSpace());
pix = kc2.data();
memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
} else {
pix = kc.data();
memcpy(iter->rawData(), pix, m_d->colorSpace()->pixelSize());
}
m_d->cache()->invalidate();
return true;
}
bool KisPaintDevice::fastBitBltPossible(KisPaintDeviceSP src)
{
return m_d->fastBitBltPossible(src);
}
void KisPaintDevice::fastBitBlt(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBlt(src, rect);
}
void KisPaintDevice::fastBitBltOldData(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltOldData(src, rect);
}
void KisPaintDevice::fastBitBltRough(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltRough(src, rect);
}
void KisPaintDevice::fastBitBltRoughOldData(KisPaintDeviceSP src, const QRect &rect)
{
m_d->currentStrategy()->fastBitBltRoughOldData(src, rect);
}
void KisPaintDevice::readBytes(quint8 * data, qint32 x, qint32 y, qint32 w, qint32 h) const
{
readBytes(data, QRect(x, y, w, h));
}
void KisPaintDevice::readBytes(quint8 *data, const QRect &rect) const
{
m_d->currentStrategy()->readBytes(data, rect);
}
void KisPaintDevice::writeBytes(const quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h)
{
writeBytes(data, QRect(x, y, w, h));
}
void KisPaintDevice::writeBytes(const quint8 *data, const QRect &rect)
{
m_d->currentStrategy()->writeBytes(data, rect);
}
QVector KisPaintDevice::readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const
{
return m_d->currentStrategy()->readPlanarBytes(x, y, w, h);
}
void KisPaintDevice::writePlanarBytes(QVector planes, qint32 x, qint32 y, qint32 w, qint32 h)
{
m_d->currentStrategy()->writePlanarBytes(planes, x, y, w, h);
}
quint32 KisPaintDevice::pixelSize() const
{
quint32 _pixelSize = m_d->colorSpace()->pixelSize();
Q_ASSERT(_pixelSize > 0);
return _pixelSize;
}
quint32 KisPaintDevice::channelCount() const
{
quint32 _channelCount = m_d->colorSpace()->channelCount();
Q_ASSERT(_channelCount > 0);
return _channelCount;
}
KisRasterKeyframeChannel *KisPaintDevice::createKeyframeChannel(const KoID &id)
{
Q_ASSERT(!m_d->framesInterface);
m_d->framesInterface.reset(new KisPaintDeviceFramesInterface(this));
Q_ASSERT(!m_d->contentChannel);
m_d->contentChannel.reset(new KisRasterKeyframeChannel(id, this, m_d->defaultBounds));
// Raster channels always have at least one frame (representing a static image)
KUndo2Command tempParentCommand;
m_d->contentChannel->addKeyframe(0, &tempParentCommand);
return m_d->contentChannel.data();
}
KisRasterKeyframeChannel* KisPaintDevice::keyframeChannel() const
{
Q_ASSERT(m_d->contentChannel);
return m_d->contentChannel.data();
}
const KoColorSpace* KisPaintDevice::colorSpace() const
{
Q_ASSERT(m_d->colorSpace() != 0);
return m_d->colorSpace();
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice() const
{
KisPaintDeviceSP device = new KisPaintDevice(compositionSourceColorSpace());
device->setDefaultBounds(defaultBounds());
return device;
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource) const
{
KisPaintDeviceSP clone = new KisPaintDevice(*cloneSource);
clone->setDefaultBounds(defaultBounds());
clone->convertTo(compositionSourceColorSpace(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
return clone;
}
KisPaintDeviceSP KisPaintDevice::createCompositionSourceDevice(KisPaintDeviceSP cloneSource, const QRect roughRect) const
{
KisPaintDeviceSP clone = new KisPaintDevice(colorSpace());
clone->setDefaultBounds(defaultBounds());
clone->makeCloneFromRough(cloneSource, roughRect);
clone->convertTo(compositionSourceColorSpace(),
KoColorConversionTransformation::internalRenderingIntent(),
KoColorConversionTransformation::internalConversionFlags());
return clone;
}
KisFixedPaintDeviceSP KisPaintDevice::createCompositionSourceDeviceFixed() const
{
return new KisFixedPaintDevice(compositionSourceColorSpace());
}
const KoColorSpace* KisPaintDevice::compositionSourceColorSpace() const
{
return colorSpace();
}
QVector KisPaintDevice::channelSizes() const
{
QVector sizes;
QList channels = colorSpace()->channels();
std::sort(channels.begin(), channels.end());
Q_FOREACH (KoChannelInfo * channelInfo, channels) {
sizes.append(channelInfo->size());
}
return sizes;
}
KisPaintDevice::MemoryReleaseObject::~MemoryReleaseObject()
{
KisDataManager::releaseInternalPools();
}
KisPaintDevice::MemoryReleaseObject* KisPaintDevice::createMemoryReleaseObject()
{
return new MemoryReleaseObject();
}
KisPaintDevice::LodDataStruct::~LodDataStruct()
{
}
QRegion KisPaintDevice::regionForLodSyncing() const
{
return m_d->regionForLodSyncing();
}
KisPaintDevice::LodDataStruct* KisPaintDevice::createLodDataStruct(int lod)
{
return m_d->createLodDataStruct(lod);
}
void KisPaintDevice::updateLodDataStruct(LodDataStruct *dst, const QRect &srcRect)
{
m_d->updateLodDataStruct(dst, srcRect);
}
void KisPaintDevice::uploadLodDataStruct(LodDataStruct *dst)
{
m_d->uploadLodDataStruct(dst);
}
KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface()
{
return m_d->framesInterface.data();
}
/******************************************************************/
/* KisPaintDeviceFramesInterface */
/******************************************************************/
KisPaintDeviceFramesInterface::KisPaintDeviceFramesInterface(KisPaintDevice *parentDevice)
: q(parentDevice)
{
}
QList KisPaintDeviceFramesInterface::frames()
{
return q->m_d->frameIds();
}
int KisPaintDeviceFramesInterface::createFrame(bool copy, int copySrc, const QPoint &offset, KUndo2Command *parentCommand)
{
return q->m_d->createFrame(copy, copySrc, offset, parentCommand);
}
void KisPaintDeviceFramesInterface::deleteFrame(int frame, KUndo2Command *parentCommand)
{
return q->m_d->deleteFrame(frame, parentCommand);
}
void KisPaintDeviceFramesInterface::fetchFrame(int frameId, KisPaintDeviceSP targetDevice)
{
q->m_d->fetchFrame(frameId, targetDevice);
}
void KisPaintDeviceFramesInterface::uploadFrame(int srcFrameId, int dstFrameId, KisPaintDeviceSP srcDevice)
{
q->m_d->uploadFrame(srcFrameId, dstFrameId, srcDevice);
}
void KisPaintDeviceFramesInterface::uploadFrame(int dstFrameId, KisPaintDeviceSP srcDevice)
{
q->m_d->uploadFrame(dstFrameId, srcDevice);
}
QRect KisPaintDeviceFramesInterface::frameBounds(int frameId)
{
return q->m_d->frameBounds(frameId);
}
QPoint KisPaintDeviceFramesInterface::frameOffset(int frameId) const
{
return q->m_d->frameOffset(frameId);
}
void KisPaintDeviceFramesInterface::setFrameDefaultPixel(const KoColor &defPixel, int frameId)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
q->m_d->setFrameDefaultPixel(defPixel, frameId);
}
KoColor KisPaintDeviceFramesInterface::frameDefaultPixel(int frameId) const
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return KoColor(Qt::red, q->m_d->colorSpace());
}
return q->m_d->frameDefaultPixel(frameId);
}
bool KisPaintDeviceFramesInterface::writeFrame(KisPaintDeviceWriter &store, int frameId)
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return false;
}
return q->m_d->writeFrame(store, frameId);
}
bool KisPaintDeviceFramesInterface::readFrame(QIODevice *stream, int frameId)
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return false;
}
return q->m_d->readFrame(stream, frameId);
}
int KisPaintDeviceFramesInterface::currentFrameId() const
{
return q->m_d->currentFrameId();
}
KisDataManagerSP KisPaintDeviceFramesInterface::frameDataManager(int frameId) const
{
KIS_ASSERT_RECOVER(frameId >= 0) {
return q->m_d->dataManager();
}
return q->m_d->frameDataManager(frameId);
}
void KisPaintDeviceFramesInterface::invalidateFrameCache(int frameId)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
return q->m_d->invalidateFrameCache(frameId);
}
void KisPaintDeviceFramesInterface::setFrameOffset(int frameId, const QPoint &offset)
{
KIS_ASSERT_RECOVER_RETURN(frameId >= 0);
return q->m_d->setFrameOffset(frameId, offset);
}
KisPaintDeviceFramesInterface::TestingDataObjects
KisPaintDeviceFramesInterface::testingGetDataObjects() const
{
TestingDataObjects objects;
objects.m_data = q->m_d->m_data.data();
objects.m_lodData = q->m_d->m_lodData.data();
objects.m_externalFrameData = q->m_d->m_externalFrameData.data();
typedef KisPaintDevice::Private::FramesHash FramesHash;
FramesHash::const_iterator it = q->m_d->m_frames.constBegin();
FramesHash::const_iterator end = q->m_d->m_frames.constEnd();
for (; it != end; ++it) {
objects.m_frames.insert(it.key(), it.value().data());
}
objects.m_currentData = q->m_d->currentData();
return objects;
}
QList KisPaintDeviceFramesInterface::testingGetDataObjectsList() const
{
return q->m_d->allDataObjects();
}
void KisPaintDevice::tesingFetchLodDevice(KisPaintDeviceSP targetDevice)
{
m_d->tesingFetchLodDevice(targetDevice);
}
diff --git a/libs/libkis/Action.cpp b/libs/libkis/Action.cpp
index 75005dce79..faafc23113 100644
--- a/libs/libkis/Action.cpp
+++ b/libs/libkis/Action.cpp
@@ -1,169 +1,158 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 "Action.h"
struct Action::Private {
Private() {}
QAction *action {0};
};
Action::Action(QObject *parent)
: QObject(parent)
, d(new Private)
{
d->action = new KisAction(this);
connect(d->action, SIGNAL(triggered(bool)), SIGNAL(triggered(bool)));
}
Action::Action(const QString &name, QAction *action, QObject *parent)
: QObject(parent)
, d(new Private)
{
d->action = action;
d->action->setObjectName(name);
connect(d->action, SIGNAL(triggered(bool)), SIGNAL(triggered(bool)));
}
Action::~Action()
{
delete d;
}
bool Action::operator==(const Action &other) const
{
return (d->action == other.d->action);
}
bool Action::operator!=(const Action &other) const
{
return !(operator==(other));
}
QString Action::text() const
{
if (!d->action) return "";
return d->action->text();
}
void Action::setText(QString text)
{
if (!d->action) return;
d->action->setText(text);
}
QString Action::name() const
{
if (!d->action) return "";
return d->action->objectName();
}
void Action::setName(QString name)
{
if (!d->action) return;
d->action->setObjectName(name);
}
bool Action::isCheckable() const
{
if (!d->action) return false;
return d->action->isCheckable();
}
void Action::setCheckable(bool value)
{
if (!d->action) return;
d->action->setCheckable(value);
}
bool Action::isChecked() const
{
if (!d->action) return false;
return d->action->isChecked();
}
void Action::setChecked(bool value)
{
if (!d->action) return;
d->action->setChecked(value);
}
QString Action::shortcut() const
{
if (!d->action) return QString();
return d->action->shortcut().toString();
}
void Action::setShortcut(QString value)
{
if (!d->action) return;
d->action->setShortcut(QKeySequence::fromString(value));
}
bool Action::isVisible() const
{
if (!d->action) return false;
return d->action->isVisible();
}
void Action::setVisible(bool value)
{
if (!d->action) return;
d->action->setVisible(value);
}
bool Action::isEnabled() const
{
if (!d->action) return false;
return d->action->isEnabled();
}
void Action::setEnabled(bool value)
{
if (!d->action) return;
d->action->setEnabled(value);
}
void Action::setToolTip(QString tooltip)
{
if (!d->action) return;
d->action->setToolTip(tooltip);
}
QString Action::tooltip() const
{
return d->action->toolTip();
}
void Action::trigger()
{
d->action->trigger();
}
-
-
-void Action::setMenu(const QString menu)
-{
- d->action->setProperty("menu", menu);
-}
-
-QString Action::menu() const
-{
- return d->action->property("menu").toString();
-}
diff --git a/libs/libkis/Action.h b/libs/libkis/Action.h
index 3d8b470013..9546fc644d 100644
--- a/libs/libkis/Action.h
+++ b/libs/libkis/Action.h
@@ -1,165 +1,153 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 LIBKIS_ACTION_H
#define LIBKIS_ACTION_H
#include
#include "kritalibkis_export.h"
#include "libkis.h"
/**
* Action encapsulates a KisAction. By default, actions are put in the Tools/Scripts menu.
*/
class KRITALIBKIS_EXPORT Action : public QObject
{
Q_OBJECT
public:
/**
* @brief Action Create a new action object
* @param parent the parent if it's in a QObject hierarchy
*/
explicit Action(QObject *parent = 0);
/**
* @brief Action Create a new action object
* @param name the name of the action
* @param action the QAction it wraps
* @param parent the parent if it's in a QObject hierarchy
*/
Action(const QString &name, QAction *action, QObject *parent = 0);
~Action() override;
bool operator==(const Action &other) const;
bool operator!=(const Action &other) const;
public Q_SLOTS:
/**
* @return the user-visible text of the action.
*/
QString text() const;
/**
* set the user-visible text of the action to @param text.
*/
void setText(QString text);
/**
* @return the internal name of the action.
*/
QString name() const;
/**
* set the name of the action to @param name. This is not the user-visible name, but the internal one
*/
void setName(QString name);
/**
* @return true if the action is checkable, false if it isn't*
*/
bool isCheckable() const;
/**
* Set the action action checkable if @param value is true, unchecked if it's false
*/
void setCheckable(bool value);
/**
* @return true if the action is checked, false if it isn't
*/
bool isChecked() const;
/**
* Set the action checked if @param value is true, unchecked if it's false
*/
void setChecked(bool value);
/**
* Return the action's shortcut as a string
*/
QString shortcut() const;
/**
* set the action's shortcut to the given string.
* @code
* action.setShortcut("CTRL+SHIFT+S")
* @endcode
*/
void setShortcut(QString value);
bool isVisible() const;
/**
* @brief setVisible determines whether the action will be visible in the scripting menu.
* @param value true if the action is to be shown in the menu, false otherwise
*/
void setVisible(bool value);
/**
* @return true if the action is enabled, false if not
*/
bool isEnabled() const;
/**
* Set the action enabled or disabled according to @param value
*/
void setEnabled(bool value);
/**
* Set the tooltip to the given @param tooltip
*/
void setToolTip(QString tooltip);
/**
* @return the tooltip text
*/
QString tooltip() const;
/**
* Trigger this action
*/
void trigger();
- /**
- * @brief setMenu determines in which menu the action will be placed. The default is tools/scripts
- * @param menu the menu where the action should go, / -separated to drill down the hierarchy
- */
- void setMenu(const QString menu);
-
- /**
- * @return the menu in which this action is to be placed.
- */
- QString menu() const;
-
-
Q_SIGNALS:
/**
* Emitted whenever the action is triggered.
*/
void triggered(bool);
private:
struct Private;
Private *const d;
};
#endif // LIBKIS_ACTION_H
diff --git a/libs/libkis/Extension.h b/libs/libkis/Extension.h
index fc9d76c47e..eb9502a953 100644
--- a/libs/libkis/Extension.h
+++ b/libs/libkis/Extension.h
@@ -1,78 +1,86 @@
/*
* Copyright (c) 2015 Cyrille Berger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 LIBKIS_EXTENSION_H
#define LIBKIS_EXTENSION_H
#include "kritalibkis_export.h"
#include
+#include
/**
- * An Extension is the base for classes that extend Krita. An Extension
+ * An Extension is the base for classes that extend Krita. An Extension
* is loaded on startup, when the setup() method will be executed.
- *
- * The extension instance should be added to the Krita Application object
+ *
+ * The extension instance should be added to the Krita Application object
* using Krita.instance().addViewExtension or Application.addViewExtension
* or Scripter.addViewExtension.
- *
+ *
* Example:
- *
+ *
* @code
* import sys
* from PyQt5.QtGui import *
* from PyQt5.QtWidgets import *
* from krita import *
* class HelloExtension(Extension):
*
* def __init__(self, parent):
* super().__init__(parent)
*
* def hello(self):
* QMessageBox.information(QWidget(), "Test", "Hello! This is Krita " + Application.version())
- *
+ *
* def setup(self):
* qDebug("Hello Setup")
- * action = Krita.instance().createAction("hello")
+ *
+ * def createActions(self, window)
+ * action = window.createAction("hello")
* action.triggered.connect(self.hello)
*
* Scripter.addExtension(HelloExtension(Krita.instance()))
- *
+ *
* @endcode
*/
class KRITALIBKIS_EXPORT Extension : public QObject
{
Q_OBJECT
public:
-
+
/**
- * Create a new extension. The extension will be
+ * Create a new extension. The extension will be
* owned by @param parent.
*/
explicit Extension(QObject *parent = 0);
~Extension() override;
-
+
/**
- * Override this function to setup your Extension. You can use it to add
- * Actions to the action collection or integrate in any other way with
- * the application.
+ * Override this function to setup your Extension. You can use it to integrate
+ * with the Krita application instance.
*/
virtual void setup() = 0;
+
+ virtual void createActions(Window *window) = 0;
+
};
+
+
+
#endif
diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp
index b9421a573e..a90f5ec0ea 100644
--- a/libs/libkis/Krita.cpp
+++ b/libs/libkis/Krita.cpp
@@ -1,433 +1,423 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 "Krita.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include "View.h"
#include "Document.h"
#include "Window.h"
#include "Extension.h"
#include "DockWidgetFactoryBase.h"
#include "Filter.h"
#include "InfoObject.h"
#include "Resource.h"
Krita* Krita::s_instance = 0;
struct Krita::Private {
Private() {}
QList extensions;
bool batchMode {false};
Notifier *notifier{new Notifier()};
};
Krita::Krita(QObject *parent)
: QObject(parent)
, d(new Private)
{
qRegisterMetaType();
+ connect(KisPart::instance(), SIGNAL(sigWindowAdded(KisMainWindow*)), SLOT(mainWindowAdded(KisMainWindow*)));
}
Krita::~Krita()
{
qDeleteAll(d->extensions);
delete d->notifier;
delete d;
}
QList Krita::actions() const
{
QList actionList;
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return actionList;
}
KActionCollection *actionCollection = mainWindow->actionCollection();
Q_FOREACH(QAction *action, actionCollection->actions()) {
actionList << new Action(action->objectName(), action);
}
return actionList;
}
Action *Krita::action(const QString &name) const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
KActionCollection *actionCollection = mainWindow->actionCollection();
QAction *action = actionCollection->action(name);
if (action) {
return new Action(name, action);
}
return 0;
}
Document* Krita::activeDocument() const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
KisView *view = mainWindow->activeView();
if (!view) {
return 0;
}
KisDocument *document = view->document();
return new Document(document);
}
void Krita::setActiveDocument(Document* value)
{
Q_FOREACH(KisView *view, KisPart::instance()->views()) {
if (view->document() == value->document().data()) {
view->activateWindow();
break;
}
}
}
bool Krita::batchmode() const
{
return d->batchMode;
}
void Krita::setBatchmode(bool value)
{
d->batchMode = value;
}
QList Krita::documents() const
{
QList ret;
foreach(QPointer doc, KisPart::instance()->documents()) {
ret << new Document(doc);
}
return ret;
}
QStringList Krita::filters() const
{
QStringList ls = KisFilterRegistry::instance()->keys();
std::sort(ls.begin(), ls.end());
return ls;
}
Filter *Krita::filter(const QString &name) const
{
if (!filters().contains(name)) return 0;
Filter *filter = new Filter();
filter->setName(name);
KisFilterSP f = KisFilterRegistry::instance()->value(name);
KisFilterConfigurationSP fc = f->defaultConfiguration();
InfoObject *info = new InfoObject(fc);
filter->setConfiguration(info);
return filter;
}
QStringList Krita::colorModels() const
{
QSet colorModelsIds;
QList ids = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces);
Q_FOREACH(KoID id, ids) {
colorModelsIds << id.id();
}
return colorModelsIds.toList();;
}
QStringList Krita::colorDepths(const QString &colorModel) const
{
QSet colorDepthsIds;
QList ids = KoColorSpaceRegistry::instance()->colorDepthList(colorModel, KoColorSpaceRegistry::AllColorSpaces);
Q_FOREACH(KoID id, ids) {
colorDepthsIds << id.id();
}
return colorDepthsIds.toList();;
}
QStringList Krita::filterStrategies() const
{
return KisFilterStrategyRegistry::instance()->keys();
}
QStringList Krita::profiles(const QString &colorModel, const QString &colorDepth) const
{
QSet profileNames;
QString id = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth);
QList profiles = KoColorSpaceRegistry::instance()->profilesFor(id);
Q_FOREACH(const KoColorProfile *profile, profiles) {
profileNames << profile->name();
}
return profileNames.toList();
}
bool Krita::addProfile(const QString &profilePath)
{
KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc");
return iccEngine->addProfile(profilePath);
}
Notifier* Krita::notifier() const
{
return d->notifier;
}
QString Krita::version() const
{
return KritaVersionWrapper::versionString(true);
}
QList Krita::views() const
{
QList ret;
foreach(QPointer view, KisPart::instance()->views()) {
ret << new View(view);
}
return ret;
}
Window *Krita::activeWindow() const
{
KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow();
if (!mainWindow) {
return 0;
}
return new Window(mainWindow);
}
QList Krita::windows() const
{
QList ret;
foreach(QPointer mainWin, KisPart::instance()->mainWindows()) {
ret << new Window(mainWin);
}
return ret;
}
QMap Krita::resources(const QString &type) const
{
QMap resources = QMap ();
if (type.toLower() == "pattern") {
KoResourceServer* server = KoResourceServerProvider::instance()->patternServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "gradient") {
KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "brush") {
KisBrushResourceServer* server = KisBrushServer::instance()->brushServer();
Q_FOREACH (KisBrushSP res, server->resources()) {
resources[res->name()] = new Resource(res.data());
}
}
else if (type.toLower() == "preset") {
KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer();
Q_FOREACH (KisPaintOpPresetSP res, server->resources()) {
resources[res->name()] = new Resource(res.data());
}
}
else if (type.toLower() == "palette") {
KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
else if (type.toLower() == "workspace") {
KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer();
Q_FOREACH (KoResource *res, server->resources()) {
resources[res->name()] = new Resource(res);
}
}
return resources;
}
QStringList Krita::recentDocuments() const
{
KConfigGroup grp = KSharedConfig::openConfig()->group(QString("RecentFiles"));
QStringList keys = grp.keyList();
QStringList recentDocuments;
for(int i = 0; i <= keys.filter("File").count(); i++)
recentDocuments << grp.readEntry(QString("File%1").arg(i), QString(""));
return recentDocuments;
}
Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution)
{
KisDocument *document = KisPart::instance()->createDocument();
KisPart::instance()->addDocument(document);
const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile);
Q_ASSERT(cs);
QColor qc(Qt::white);
qc.setAlpha(0);
KoColor bgColor(qc, cs);
if (!document->newImage(name, width, height, cs, bgColor, true, 1, "", double(resolution / 72) )) {
qDebug() << "Could not create a new image";
return 0;
}
Q_ASSERT(document->image());
qDebug() << document->image()->objectName();
return new Document(document);
}
Document* Krita::openDocument(const QString &filename)
{
KisDocument *document = KisPart::instance()->createDocument();
KisPart::instance()->addDocument(document);
document->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent);
return new Document(document);
}
Window* Krita::openWindow()
{
KisMainWindow *mw = KisPart::instance()->createMainWindow();
return new Window(mw);
}
-Action *Krita::createAction(const QString &id, const QString &text, bool addToScriptMenu)
-{
- KisAction *action = new KisAction(text, this);
- action->setObjectName(id);
-
- KisActionRegistry *actionRegistry = KisActionRegistry::instance();
- actionRegistry->propertizeAction(action->objectName(), action);
- bool ok; // We will skip this check
- int activationFlags = actionRegistry->getActionProperty(id, "activationFlags").toInt(&ok, 2);
- int activationConditions = actionRegistry->getActionProperty(id, "activationConditions").toInt(&ok, 2);
- action->setActivationFlags((KisAction::ActivationFlags) activationFlags);
- action->setActivationConditions((KisAction::ActivationConditions) activationConditions);
-
- if (addToScriptMenu) {
- KisPart::instance()->addScriptAction(action);
- }
- return new Action(action->objectName(), action);
-}
-
void Krita::addExtension(Extension* extension)
{
d->extensions.append(extension);
}
QList< Extension* > Krita::extensions()
{
return d->extensions;
}
void Krita::writeSetting(const QString &group, const QString &name, const QString &value)
{
KConfigGroup grp = KSharedConfig::openConfig()->group(group);
grp.writeEntry(name, value);
}
QString Krita::readSetting(const QString &group, const QString &name, const QString &defaultValue)
{
KConfigGroup grp = KSharedConfig::openConfig()->group(group);
return grp.readEntry(name, defaultValue);
}
QIcon Krita::icon(QString &iconName) const
{
return KisIconUtils::loadIcon(iconName);
}
void Krita::addDockWidgetFactory(DockWidgetFactoryBase* factory)
{
KoDockRegistry::instance()->add(factory);
}
Krita* Krita::instance()
{
if (!s_instance)
{
s_instance = new Krita;
}
return s_instance;
}
/**
* Scripter.fromVariant(variant)
* variant is a QVariant
* returns instance of QObject-subclass
*
* This is a helper method for PyQt because PyQt cannot cast a variant to a QObject or QWidget
*/
QObject *Krita::fromVariant(const QVariant& v)
{
if (v.canConvert< QWidget* >())
{
QObject* obj = qvariant_cast< QWidget* >(v);
return obj;
}
else if (v.canConvert< QObject* >())
{
QObject* obj = qvariant_cast< QObject* >(v);
return obj;
}
else
return 0;
}
+
+void Krita::mainWindowAdded(KisMainWindow *kisWindow)
+{
+ Q_FOREACH(Extension *extension, d->extensions) {
+ Window window(kisWindow);
+ extension->createActions(&window);
+ }
+}
diff --git a/libs/libkis/Krita.h b/libs/libkis/Krita.h
index fc4f5b6a36..5fd47153fb 100644
--- a/libs/libkis/Krita.h
+++ b/libs/libkis/Krita.h
@@ -1,350 +1,344 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 LIBKIS_KRITA_H
#define LIBKIS_KRITA_H
#include
#include "kritalibkis_export.h"
#include "libkis.h"
#include "Extension.h"
#include "Document.h"
#include "Window.h"
#include "View.h"
#include "Action.h"
#include "Notifier.h"
class QAction;
/**
* Krita is a singleton class that offers the root access to the Krita object hierarchy.
*
* The Krita.instance() is aliased as two builtins: Scripter and Application.
*/
class KRITALIBKIS_EXPORT Krita : public QObject
{
Q_OBJECT
public:
explicit Krita(QObject *parent = 0);
~Krita() override;
public Q_SLOTS:
/**
* @return the currently active document, if there is one.
*/
Document* activeDocument() const;
/**
* @brief setActiveDocument activates the first view that shows the given document
* @param value the document we want to activate
*/
void setActiveDocument(Document* value);
/**
* @brief batchmode determines whether the script is run in batch mode. If batchmode
* is true, scripts should now show messageboxes or dialog boxes.
*
* Note that this separate from Document.setBatchmode(), which determines whether
* export/save option dialogs are shown.
*
* @return true if the script is run in batchmode
*/
bool batchmode() const;
/**
* @brief setBatchmode sets the batchmode to @param value; if true, scripts should
* not show dialogs or messageboxes.
*/
void setBatchmode(bool value);
/**
* @return return a list of all actions for the currently active mainWindow.
*/
QList actions() const;
/**
* @return the action that has been registered under the given name, or 0 if no such action exists.
*/
Action *action(const QString &name) const;
/**
* @return a list of all open Documents
*/
QList documents() const;
/**
* @brief Filters are identified by an internal name. This function returns a list
* of all existing registered filters.
* @return a list of all registered filters
*/
QStringList filters() const;
/**
* @brief filter construct a Filter object with a default configuration.
* @param name the name of the filter. Use Krita.instance().filters() to get
* a list of all possible filters.
* @return the filter or None if there is no such filter.
*/
Filter *filter(const QString &name) const;
/**
* @brief colorModels creates a list with all color models id's registered.
* @return a list of all color models or a empty list if there is no such color models.
*/
QStringList colorModels() const;
/**
* @brief colorDepths creates a list with the names of all color depths
* compatible with the given color model.
* @param colorModel the id of a color model.
* @return a list of all color depths or a empty list if there is no such
* color depths.
*/
QStringList colorDepths(const QString &colorModel) const;
/**
* @brief filterStrategies Retrieves all installed filter strategies. A filter
* strategy is used when transforming (scaling, shearing, rotating) an image to
* calculate the value of the new pixels. You can use th
* @return the id's of all available filters.
*/
QStringList filterStrategies() const;
/**
* @brief profiles creates a list with the names of all color profiles compatible
* with the given color model and color depth.
* @param colorModel A string describing the color model of the image:
*
* - A: Alpha mask
* - RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
* - XYZA: XYZ with alpha channel
* - LABA: LAB with alpha channel
* - CMYKA: CMYK with alpha channel
* - GRAYA: Gray with alpha channel
* - YCbCrA: YCbCr with alpha channel
*
* @param colorDepth A string describing the color depth of the image:
*
* - U8: unsigned 8 bits integer, the most common type
* - U16: unsigned 16 bits integer
* - F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
* - F32: 32 bits floating point
*
* @return a list with valid names
*/
QStringList profiles(const QString &colorModel, const QString &colorDepth) const;
/**
* @brief addProfile load the given profile into the profile registry.
* @param profilePath the path to the profile.
* @return true if adding the profile succeeded.
*/
bool addProfile(const QString &profilePath);
/**
* @brief notifier the Notifier singleton emits signals when documents are opened and
* closed, the configuration changes, views are opened and closed or windows are opened.
* @return the notifier object
*/
Notifier *notifier() const;
/**
* @brief version Determine the version of Krita
*
* Usage: print(Application.version ())
*
* @return the version string including git sha1 if Krita was built from git
*/
QString version() const;
/**
* @return a list of all views. A Document can be shown in more than one view.
*/
QList views() const;
/**
* @return the currently active window or None if there is no window
*/
Window *activeWindow() const;
/**
* @return a list of all windows
*/
QList windows() const;
/**
* @brief resources returns a list of Resource objects of the given type
* @param type Valid types are:
*
*
* - pattern
* - gradient
* - brush
* - preset
* - palette
* - workspace
*
*/
QMap resources(const QString &type) const;
/**
* @brief return all recent documents registered in the RecentFiles group of the kritarc
*/
QStringList recentDocuments() const;
/**
* @brief createDocument creates a new document and image and registers
* the document with the Krita application.
*
* Unless you explicitly call Document::close() the document wil remain
* known to the Krita document registry. The document and its image will
* only be deleted when Krita exits.
*
* The document will have one transparent layer.
*
* To create a new document and show it, do something like:
@code
from Krita import *
def add_document_to_window():
d = Application.createDocument(100, 100, "Test", "RGBA", "U8", "", 120.0)
Application.activeWindow().addView(d)
add_document_to_window()
@endcode
*
* @param width the width in pixels
* @param height the height in pixels
* @param name the name of the image (not the filename of the document)
* @param colorModel A string describing the color model of the image:
*
* - A: Alpha mask
* - RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
* - XYZA: XYZ with alpha channel
* - LABA: LAB with alpha channel
* - CMYKA: CMYK with alpha channel
* - GRAYA: Gray with alpha channel
* - YCbCrA: YCbCr with alpha channel
*
* @param colorDepth A string describing the color depth of the image:
*
* - U8: unsigned 8 bits integer, the most common type
* - U16: unsigned 16 bits integer
* - F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
* - F32: 32 bits floating point
*
* @param profile The name of an icc profile that is known to Krita. If an empty string is passed, the default is
* taken.
* @param resolution the resolution in points per inch.
* @return the created document.
*/
Document *createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution);
/**
* @brief openDocument creates a new Document, registers it with the Krita application and loads the given file.
* @param filename the file to open in the document
* @return the document
*/
Document *openDocument(const QString &filename);
/**
* @brief openWindow create a new main window. The window is not shown by default.
*/
Window *openWindow();
- /**
- * @brief createAction creates an action with the given text and passes it to Krita. Every newly created
- * mainwindow will create an instance of this action. This means that actions need to be created in the
- * setup phase of the plugin, not on the fly.
- * @param id the unique id for this action
- * @param text the user-visible text
- * @return the Action you can connect a slot to.
- */
- Action *createAction(const QString &name, const QString &text, bool addToScriptMenu = true);
-
/**
* @brief addExtension add the given plugin to Krita. There will be a single instance of each Extension in the Krita process.
* @param extension the extension to add.
*/
void addExtension(Extension* extension);
/**
* return a list with all registered extension objects.
*/
QList extensions();
/**
* @brief addDockWidgetFactory Add the given docker factory to the application. For scripts
* loaded on startup, this means that every window will have one of the dockers created by the
* factory.
* @param factory The factory object.
*/
void addDockWidgetFactory(DockWidgetFactoryBase* factory );
/**
* @brief writeSetting write the given setting under the given name to the kritarc file in
* the given settings group.
* @param group The group the setting belongs to. If empty, then the setting is written in the
* general section
* @param name The name of the setting
* @param value The value of the setting. Script settings are always written as strings.
*/
void writeSetting(const QString &group, const QString &name, const QString &value);
/**
* @brief readSetting read the given setting value from the kritarc file.
* @param group The group the setting is part of. If empty, then the setting is read from
* the general group.
* @param name The name of the setting
* @param defaultValue The default value of the setting
* @return a string representing the setting.
*/
QString readSetting(const QString &group, const QString &name, const QString &defaultValue);
/**
* @brief icon
* This allows you to get icons from Krita's internal icons.
* @param iconName name of the icon.
* @return the icon related to this name.
*/
QIcon icon(QString &iconName) const;
/**
* @brief instance retrieve the singleton instance of the Application object.
*/
static Krita* instance();
// Internal only: for use with mikro.py
static QObject *fromVariant(const QVariant& v);
+private Q_SLOTS:
+
+ void mainWindowAdded(KisMainWindow *window);
+
private:
struct Private;
Private *const d;
static Krita* s_instance;
};
Q_DECLARE_METATYPE(Notifier*);
#endif // LIBKIS_KRITA_H
diff --git a/libs/libkis/Mainpage.dox b/libs/libkis/Mainpage.dox
index 03b09767d3..c98cbcf7c8 100644
--- a/libs/libkis/Mainpage.dox
+++ b/libs/libkis/Mainpage.dox
@@ -1,147 +1,150 @@
/*
* Copyright (C) 2016 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.
*/
/**
\mainpage Krita Scripting and Plugin Wrapper Library: libkis
Libkis is a QObject-based wrapper around Krita's internal libraries.
The wrapper can be used from C++ plugins that do not need the
advanced and volatile internal libraries of Krita, or can be bound
to scripting languages like Python or Javascript.
All classes are based on QObject, so QMetaObject introspection can
be used to discover properties, slots and signals and automatically
expose all functionality to the client.
Note that all objects that are created are wrapper objects that are
owned by the scripting environment or the plugin.
Using the functionality in this library, either through a scripting
environment like Python or Javascript, or directly from C++, you can,
among other things achieve the following functionality:
- Open, save, export, rename files.
- Access the layers and masks in a file
- Read and write pixel data
- Add menu items, toolbar items and docker palettes
The reference implementation of scripting in Krita is implemented
in Python 3. All examples in the documentation for scripting will be
provided using Python, although the api documentation is generated
from C++ header files and shows c++ syntax for arguments.
Autostarting Scripts
====================
Autostarting scripts or script-based plugins are scripts that Krita
loads on startup. You can add autostarting scripts to Krita by placing
them in the pykrita folder in the resources folder: go to settings/
manage resources and press the Open Resources Folder to open your
local resources folder.
Scripts are identified by a file that ends in a `.desktop` extension
that contain information about the script itself. For example:
@code
[Desktop Entry]
Type=Service
ServiceTypes=Krita/PythonPlugin
X-KDE-Library=hello
X-Python-2-Compatible=false
Name=Hello World
Comment=Basic plugin to test PyKrita
@endcode
The Python code itself should be placed in the pykrita/hello folder.
Your Python plugin needs to be a module, so needs to have a `__init__.py`
file:
@code
# let's make a module
from .hello import *
@endcode
Krita is a Qt-based application. In principle, you can use any Python
binding to Qt as long as it's using exactly the same version of Qt
that Krita uses. In our examples we will be using [PyQt](https://www.riverbankcomputing.com/software/pyqt/intro).
The easiest access to the Krita api is by simply importing the "krita"
module. This automatically adds two built-ins: Scripter and Application.
This is an alias for Krita.instance(), which is the first place from
which to access the running Krita instance.
@code
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from krita import *
def hello():
QMessageBox.information(QWidget(), "Test", "Hello World")
class HelloExtension(Extension):
def __init__(self, parent):
super().__init__(parent)
- def setup(self, viewManager):
- action = viewManager.createAction("Hello")
+ def setup(self):
+ pass
+
+ def createActions(self, window):
+ action = window.createAction("Hello")
action.triggered.connect(hello)
Krita.instance().addExtension(HelloExtension(Krita.instance()))
@endcode
The Krita Object Model
======================
The starting point is the @see Krita class, which provides a singleton
object for easy reference. From the Krita class, a hierarchy is provided
where you can access windows, lviews, documents, nodes and channels.
You can access the Krita class as
* Krita.instance()
* Application
* Scripter
For ease of use.
The Document class is provided to allow access to the images Krita has
loaded. *Note*: internally, Krita distinguishes between images and documents.
A document contains an image and knows the filename of the image, the image
itself only knows about the layers and masks it contains. The generic
name for layers and masks is *node*.
A Node can be a group layer, a file layer, a vector layer, a filter layer,
a generator layer, a clone layer, a paint layer or a transform mask, a selection
mask, a transparency mask or a colorize mask. You can change some properties
of Nodes, but not all of them.
The Window class is provided to allow access to the windows and views Krita
has open. A given Document can be shown in more than one View, and in more than
one Window. You can open and close windows and views.
*/
// DOXYGEN_SET_PROJECT_NAME = Krita
// DOXYGEN_SET_IGNORE_PREFIX = Kis Ko K
diff --git a/libs/libkis/Window.cpp b/libs/libkis/Window.cpp
index e5b2189e77..59d273e1b1 100644
--- a/libs/libkis/Window.cpp
+++ b/libs/libkis/Window.cpp
@@ -1,117 +1,151 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 "Window.h"
+#include
+#include
+
#include
#include
#include
+#include
+#include
#include
#include
-
+#include
struct Window::Private {
Private() {}
QPointer window;
};
Window::Window(KisMainWindow *window, QObject *parent)
: QObject(parent)
, d(new Private)
{
d->window = window;
connect(window, SIGNAL(destroyed(QObject*)), SIGNAL(windowClosed()));
}
Window::~Window()
{
delete d;
}
bool Window::operator==(const Window &other) const
{
return (d->window == other.d->window);
}
bool Window::operator!=(const Window &other) const
{
return !(operator==(other));
}
QMainWindow *Window::qwindow() const
{
return d->window;
}
QList Window::views() const
{
QList ret;
if (d->window) {
foreach(QPointer view, KisPart::instance()->views()) {
if (view->mainWindow() == d->window) {
ret << new View(view);
}
}
}
return ret;
}
View *Window::addView(Document *document)
{
if (d->window) {
KisView *view = d->window->newView(document->document());
return new View(view);
}
return 0;
}
void Window::showView(View *view)
{
if (views().contains(view)) {
KisView *v = view->view();
d->window->showView(v);
}
}
View *Window::activeView() const
{
if (d->window) {
return new View(d->window->activeView());
}
return 0;
}
void Window::activate()
{
if (d->window) {
d->window->activateWindow();
}
}
void Window::close()
{
if (d->window) {
KisPart::instance()->removeMainWindow(d->window);
d->window->close();
}
}
+Action *Window::createAction(const QString &id, const QString &text, const QString &menuLocation)
+{
+ KisAction *action = d->window->viewManager()->actionManager()->createAction(id);
+ action->setText(text);
+ action->setObjectName(id);
+ if (!menuLocation.isEmpty()) {
+ QAction *found = 0;
+ QList candidates = d->window->menuBar()->actions();
+ Q_FOREACH(const QString &name, menuLocation.split("/")) {
+ Q_FOREACH(QAction *candidate, candidates) {
+ if (candidate->objectName() == name) {
+ found = candidate;
+ candidates = candidate->menu()->actions();
+ break;
+ }
+ }
+ if (candidates.isEmpty()) {
+ break;
+ }
+ }
+
+ if (found && found->menu()) {
+ found->menu()->addAction(action);
+ }
+ }
+ return new Action(action->objectName(), action);
+}
+
+
diff --git a/libs/libkis/Window.h b/libs/libkis/Window.h
index f6a5b0eac4..53c5f13942 100644
--- a/libs/libkis/Window.h
+++ b/libs/libkis/Window.h
@@ -1,95 +1,112 @@
/*
* Copyright (c) 2016 Boudewijn Rempt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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 Lesser 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 LIBKIS_WINDOW_H
#define LIBKIS_WINDOW_H
#include
+#include
#include
#include "kritalibkis_export.h"
#include "libkis.h"
#include
+
+class Action;
+
/**
* Window represents one Krita mainwindow. A window can have any number
* of views open on any number of documents.
*/
class KRITALIBKIS_EXPORT Window : public QObject
{
Q_OBJECT
public:
explicit Window(KisMainWindow *window, QObject *parent = 0);
~Window() override;
bool operator==(const Window &other) const;
bool operator!=(const Window &other) const;
public Q_SLOTS:
/**
* Return a handle to the QMainWindow widget. This is useful
* to e.g. parent dialog boxes and message box.
*/
QMainWindow *qwindow() const;
/**
* @return a list of open views in this window
*/
QList views() const;
/**
* Open a new view on the given document in this window
*/
View *addView(Document *document);
/**
* Make the given view active in this window. If the view
* does not belong to this window, nothing happens.
*/
void showView(View *view);
/**
* @return the currently active view or 0 if no view is active
*/
View *activeView() const;
/**
* @brief activate activates this Window.
*/
void activate();
/**
* @brief close the active window and all its Views. If there
* are no Views left for a given Document, that Document will
* also be closed.
*/
void close();
+ /**
+ * @brief createAction creates an Action object and adds it to the action
+ * manager for this Window.
+ * @param id The unique id for the action. This will be used to
+ * propertize the action if any .action file is present
+ * @param text The user-visible text of the action. If empty, the text from the
+ * .action file is used.
+ * @param menu a /-separated string that describes which menu the action should
+ * be places in. Default is "tools/scripts"
+ * @return the new action.
+ */
+ Action *createAction(const QString &id, const QString &text = QString(), const QString &menuLocation = QString("tools/scripts"));
+
Q_SIGNALS:
/// Emitted when the window is closed.
void windowClosed();
private:
struct Private;
Private *const d;
};
#endif // LIBKIS_WINDOW_H
diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index c876aedfe2..f2524f5ccb 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -1,592 +1,591 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile
${EXIV2_INCLUDE_DIR}
)
include_directories(SYSTEM
${EIGEN3_INCLUDE_DIR}
${OCIO_INCLUDE_DIR}
)
add_subdirectory( tests )
if (APPLE)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(APPKIT_LIBRARY AppKit)
endif ()
set(kritaui_LIB_SRCS
canvas/kis_canvas_widget_base.cpp
canvas/kis_canvas2.cpp
canvas/kis_canvas_updates_compressor.cpp
canvas/kis_canvas_controller.cpp
canvas/kis_paintop_transformation_connector.cpp
canvas/kis_display_color_converter.cpp
canvas/kis_display_filter.cpp
canvas/kis_exposure_gamma_correction_interface.cpp
canvas/kis_tool_proxy.cpp
canvas/kis_canvas_decoration.cc
canvas/kis_coordinates_converter.cpp
canvas/kis_grid_manager.cpp
canvas/kis_grid_decoration.cpp
canvas/kis_grid_config.cpp
canvas/kis_prescaled_projection.cpp
canvas/kis_qpainter_canvas.cpp
canvas/kis_projection_backend.cpp
canvas/kis_update_info.cpp
canvas/kis_image_patch.cpp
canvas/kis_image_pyramid.cpp
canvas/kis_infinity_manager.cpp
canvas/kis_change_guides_command.cpp
canvas/kis_guides_decoration.cpp
canvas/kis_guides_manager.cpp
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_generator_layer.cpp
dialogs/kis_dlg_file_layer.cpp
dialogs/kis_dlg_filter.cpp
dialogs/kis_dlg_stroke_selection_properties.cpp
dialogs/kis_dlg_image_properties.cc
dialogs/kis_dlg_layer_properties.cc
dialogs/kis_dlg_preferences.cc
dialogs/slider_and_spin_box_sync.cpp
dialogs/kis_dlg_blacklist_cleanup.cpp
dialogs/kis_dlg_layer_style.cpp
dialogs/kis_dlg_png_import.cpp
dialogs/kis_dlg_import_image_sequence.cpp
dialogs/kis_delayed_save_dialog.cpp
dialogs/kis_dlg_internal_color_selector.cpp
dialogs/KisSessionManagerDialog.cpp
dialogs/KisNewWindowLayoutDialog.cpp
flake/kis_node_dummies_graph.cpp
flake/kis_dummies_facade_base.cpp
flake/kis_dummies_facade.cpp
flake/kis_node_shapes_graph.cpp
flake/kis_node_shape.cpp
flake/kis_shape_controller.cpp
flake/kis_shape_layer.cc
flake/kis_shape_layer_canvas.cpp
flake/kis_shape_selection.cpp
flake/kis_shape_selection_canvas.cpp
flake/kis_shape_selection_model.cpp
flake/kis_take_all_shapes_command.cpp
brushhud/kis_uniform_paintop_property_widget.cpp
brushhud/kis_brush_hud.cpp
brushhud/kis_round_hud_button.cpp
brushhud/kis_dlg_brush_hud_config.cpp
brushhud/kis_brush_hud_properties_list.cpp
brushhud/kis_brush_hud_properties_config.cpp
kis_aspect_ratio_locker.cpp
kis_autogradient.cc
kis_bookmarked_configurations_editor.cc
kis_bookmarked_configurations_model.cc
kis_bookmarked_filter_configurations_model.cc
kis_base_option.cpp
kis_canvas_resource_provider.cpp
kis_derived_resources.cpp
kis_categories_mapper.cpp
kis_categorized_list_model.cpp
kis_categorized_item_delegate.cpp
kis_clipboard.cc
kis_config.cc
kis_config_notifier.cpp
kis_control_frame.cpp
kis_composite_ops_model.cc
kis_paint_ops_model.cpp
kis_cursor.cc
kis_cursor_cache.cpp
kis_custom_pattern.cc
kis_file_layer.cpp
kis_change_file_layer_command.h
kis_safe_document_loader.cpp
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
kis_import_catcher.cc
kis_layer_manager.cc
kis_mask_manager.cc
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
kis_node_juggler_compressed.cpp
kis_node_selection_adapter.cpp
kis_node_insertion_adapter.cpp
kis_node_model.cpp
kis_node_filter_proxy_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
kis_model_index_converter_show_all.cpp
kis_painting_assistant.cc
kis_painting_assistants_decoration.cpp
KisDecorationsManager.cpp
kis_paintop_box.cc
kis_paintop_option.cpp
kis_paintop_options_model.cpp
kis_paintop_settings_widget.cpp
kis_popup_palette.cpp
kis_png_converter.cpp
kis_preference_set_registry.cpp
- kis_script_manager.cpp
kis_resource_server_provider.cpp
KisSelectedShapesProxy.cpp
kis_selection_decoration.cc
kis_selection_manager.cc
kis_statusbar.cc
kis_zoom_manager.cc
kis_favorite_resource_manager.cpp
kis_workspace_resource.cpp
kis_action.cpp
kis_action_manager.cpp
- kis_view_plugin.cpp
+ KisActionPlugin.cpp
kis_canvas_controls_manager.cpp
kis_tooltip_manager.cpp
kis_multinode_property.cpp
kis_stopgradient_editor.cpp
kisexiv2/kis_exif_io.cpp
kisexiv2/kis_exiv2.cpp
kisexiv2/kis_iptc_io.cpp
kisexiv2/kis_xmp_io.cpp
opengl/kis_opengl.cpp
opengl/kis_opengl_canvas2.cpp
opengl/kis_opengl_canvas_debugger.cpp
opengl/kis_opengl_image_textures.cpp
opengl/kis_texture_tile.cpp
opengl/kis_opengl_shader_loader.cpp
opengl/kis_texture_tile_info_pool.cpp
kis_fps_decoration.cpp
recorder/kis_node_query_path_editor.cc
recorder/kis_recorded_action_creator.cc
recorder/kis_recorded_action_creator_factory.cc
recorder/kis_recorded_action_creator_factory_registry.cc
recorder/kis_recorded_action_editor_factory.cc
recorder/kis_recorded_action_editor_factory_registry.cc
recorder/kis_recorded_filter_action_editor.cc
recorder/kis_recorded_filter_action_creator.cpp
recorder/kis_recorded_paint_action_editor.cc
tool/kis_selection_tool_helper.cpp
tool/kis_selection_tool_config_widget_helper.cpp
tool/kis_rectangle_constraint_widget.cpp
tool/kis_shape_tool_helper.cpp
tool/kis_tool.cc
tool/kis_delegated_tool_policies.cpp
tool/kis_tool_freehand.cc
tool/kis_speed_smoother.cpp
tool/kis_painting_information_builder.cpp
tool/kis_stabilized_events_sampler.cpp
tool/kis_tool_freehand_helper.cpp
tool/kis_tool_multihand_helper.cpp
tool/kis_figure_painting_tool_helper.cpp
tool/kis_recording_adapter.cpp
tool/kis_tool_paint.cc
tool/kis_tool_shape.cc
tool/kis_tool_ellipse_base.cpp
tool/kis_tool_rectangle_base.cpp
tool/kis_tool_polyline_base.cpp
tool/kis_tool_utils.cpp
tool/kis_resources_snapshot.cpp
tool/kis_smoothing_options.cpp
tool/KisStabilizerDelayedPaintHelper.cpp
tool/KisStrokeSpeedMonitor.cpp
tool/strokes/freehand_stroke.cpp
tool/strokes/KisStrokeEfficiencyMeasurer.cpp
tool/strokes/kis_painter_based_stroke_strategy.cpp
tool/strokes/kis_filter_stroke_strategy.cpp
tool/strokes/kis_color_picker_stroke_strategy.cpp
tool/strokes/KisFreehandStrokeInfo.cpp
tool/strokes/KisMaskedFreehandStrokePainter.cpp
tool/strokes/KisMaskingBrushRenderer.cpp
tool/strokes/KisMaskingBrushCompositeOpFactory.cpp
widgets/kis_cmb_composite.cc
widgets/kis_cmb_contour.cpp
widgets/kis_cmb_gradient.cpp
widgets/kis_paintop_list_widget.cpp
widgets/kis_cmb_idlist.cc
widgets/kis_color_space_selector.cc
widgets/kis_advanced_color_space_selector.cc
widgets/kis_cie_tongue_widget.cpp
widgets/kis_tone_curve_widget.cpp
widgets/kis_curve_widget.cpp
widgets/kis_custom_image_widget.cc
widgets/kis_image_from_clipboard_widget.cpp
widgets/kis_double_widget.cc
widgets/kis_filter_selector_widget.cc
widgets/kis_gradient_chooser.cc
widgets/kis_gradient_slider_widget.cc
widgets/kis_gradient_slider.cpp
widgets/kis_iconwidget.cc
widgets/kis_mask_widgets.cpp
widgets/kis_meta_data_merge_strategy_chooser_widget.cc
widgets/kis_multi_bool_filter_widget.cc
widgets/kis_multi_double_filter_widget.cc
widgets/kis_multi_integer_filter_widget.cc
widgets/kis_multipliers_double_slider_spinbox.cpp
widgets/kis_paintop_presets_popup.cpp
widgets/kis_tool_options_popup.cpp
widgets/kis_paintop_presets_chooser_popup.cpp
widgets/kis_paintop_presets_save.cpp
widgets/kis_paintop_preset_icon_library.cpp
widgets/kis_pattern_chooser.cc
widgets/kis_popup_button.cc
widgets/kis_preset_chooser.cpp
widgets/kis_progress_widget.cpp
widgets/kis_selection_options.cc
widgets/kis_scratch_pad.cpp
widgets/kis_scratch_pad_event_filter.cpp
widgets/kis_preset_selector_strip.cpp
widgets/kis_slider_spin_box.cpp
widgets/KisSelectionPropertySlider.cpp
widgets/kis_size_group.cpp
widgets/kis_size_group_p.cpp
widgets/kis_wdg_generator.cpp
widgets/kis_workspace_chooser.cpp
widgets/kis_categorized_list_view.cpp
widgets/kis_widget_chooser.cpp
widgets/kis_tool_button.cpp
widgets/kis_floating_message.cpp
widgets/kis_lod_availability_widget.cpp
widgets/kis_color_label_selector_widget.cpp
widgets/kis_color_filter_combo.cpp
widgets/kis_elided_label.cpp
widgets/kis_stopgradient_slider_widget.cpp
widgets/kis_spinbox_color_selector.cpp
widgets/kis_screen_color_picker.cpp
widgets/kis_preset_live_preview_view.cpp
widgets/KoDualColorButton.cpp
widgets/kis_color_input.cpp
widgets/kis_color_button.cpp
widgets/KisVisualColorSelector.cpp
widgets/KisVisualColorSelectorShape.cpp
widgets/KisVisualEllipticalSelectorShape.cpp
widgets/KisVisualRectangleSelectorShape.cpp
widgets/KisVisualTriangleSelectorShape.cpp
widgets/KoStrokeConfigWidget.cpp
widgets/KoFillConfigWidget.cpp
widgets/KisLayoutSelector.cpp
utils/kis_document_aware_spin_box_unit_manager.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
input/kis_extended_modifiers_mapper.cpp
input/kis_abstract_input_action.cpp
input/kis_tool_invocation_action.cpp
input/kis_pan_action.cpp
input/kis_alternate_invocation_action.cpp
input/kis_rotate_canvas_action.cpp
input/kis_zoom_action.cpp
input/kis_change_frame_action.cpp
input/kis_gamma_exposure_action.cpp
input/kis_show_palette_action.cpp
input/kis_change_primary_setting_action.cpp
input/kis_abstract_shortcut.cpp
input/kis_native_gesture_shortcut.cpp
input/kis_single_action_shortcut.cpp
input/kis_stroke_shortcut.cpp
input/kis_shortcut_matcher.cpp
input/kis_select_layer_action.cpp
input/KisQtWidgetsTweaker.cpp
input/KisInputActionGroup.cpp
operations/kis_operation.cpp
operations/kis_operation_configuration.cpp
operations/kis_operation_registry.cpp
operations/kis_operation_ui_factory.cpp
operations/kis_operation_ui_widget.cpp
operations/kis_filter_selection_operation.cpp
actions/kis_selection_action_factories.cpp
actions/KisPasteActionFactory.cpp
input/kis_touch_shortcut.cpp
kis_document_undo_store.cpp
kis_transaction_based_command.cpp
kis_gui_context_command.cpp
kis_gui_context_command_p.cpp
input/kis_tablet_debugger.cpp
input/kis_input_profile_manager.cpp
input/kis_input_profile.cpp
input/kis_shortcut_configuration.cpp
input/config/kis_input_configuration_page.cpp
input/config/kis_edit_profiles_dialog.cpp
input/config/kis_input_profile_model.cpp
input/config/kis_input_configuration_page_item.cpp
input/config/kis_action_shortcuts_model.cpp
input/config/kis_input_type_delegate.cpp
input/config/kis_input_mode_delegate.cpp
input/config/kis_input_button.cpp
input/config/kis_input_editor_delegate.cpp
input/config/kis_mouse_input_editor.cpp
input/config/kis_wheel_input_editor.cpp
input/config/kis_key_input_editor.cpp
processing/fill_processing_visitor.cpp
kis_asl_layer_style_serializer.cpp
kis_psd_layer_style_resource.cpp
canvas/kis_mirror_axis.cpp
kis_abstract_perspective_grid.cpp
KisApplication.cpp
KisAutoSaveRecoveryDialog.cpp
KisDetailsPane.cpp
KisDocument.cpp
KisNodeDelegate.cpp
kis_node_view_visibility_delegate.cpp
KisNodeToolTip.cpp
KisNodeView.cpp
kis_node_view_color_scheme.cpp
KisImportExportFilter.cpp
KisFilterEntry.cpp
KisImportExportManager.cpp
KisImportExportUtils.cpp
kis_async_action_feedback.cpp
KisMainWindow.cpp
KisOpenPane.cpp
KisPart.cpp
KisPrintJob.cpp
KisTemplate.cpp
KisTemplateCreateDia.cpp
KisTemplateGroup.cpp
KisTemplates.cpp
KisTemplatesPane.cpp
KisTemplateTree.cpp
KisUndoStackAction.cpp
KisView.cpp
thememanager.cpp
kis_mainwindow_observer.cpp
KisViewManager.cpp
kis_mirror_manager.cpp
qtlockedfile/qtlockedfile.cpp
qtsingleapplication/qtlocalpeer.cpp
qtsingleapplication/qtsingleapplication.cpp
KisResourceBundle.cpp
KisResourceBundleManifest.cpp
kis_md5_generator.cpp
KisApplicationArguments.cpp
KisNetworkAccessManager.cpp
KisMultiFeedRSSModel.cpp
KisRemoteFileFetcher.cpp
KisPaletteModel.cpp
kis_palette_delegate.cpp
kis_palette_view.cpp
KisColorsetChooser.cpp
KisSaveGroupVisitor.cpp
KisWindowLayoutResource.cpp
KisWindowLayoutManager.cpp
KisSessionResource.cpp
KisReferenceImagesDecoration.cpp
KisReferenceImage.cpp
flake/KisReferenceImagesLayer.cpp
flake/KisReferenceImagesLayer.h
)
if(WIN32)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support_win.cpp
input/wintab/kis_screen_size_choice_dialog.cpp
qtlockedfile/qtlockedfile_win.cpp
input/wintab/kis_tablet_support_win8.cpp
opengl/kis_opengl_win.cpp
)
endif()
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
kis_animation_frame_cache.cpp
kis_animation_cache_populator.cpp
KisAsyncAnimationRendererBase.cpp
KisAsyncAnimationCacheRenderer.cpp
KisAsyncAnimationFramesSavingRenderer.cpp
dialogs/KisAsyncAnimationRenderDialogBase.cpp
dialogs/KisAsyncAnimationCacheRenderDialog.cpp
dialogs/KisAsyncAnimationFramesSaveDialog.cpp
canvas/kis_animation_player.cpp
kis_animation_importer.cpp
KisSyncedAudioPlayback.cpp
)
if(UNIX)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/kis_tablet_event.cpp
input/wintab/kis_tablet_support.cpp
qtlockedfile/qtlockedfile_unix.cpp
)
if(NOT APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
input/wintab/qxcbconnection_xi2.cpp
input/wintab/qxcbconnection.cpp
input/wintab/kis_xi2_event_filter.cpp
)
endif()
endif()
if(APPLE)
set(kritaui_LIB_SRCS
${kritaui_LIB_SRCS}
osx.mm
)
endif()
ki18n_wrap_ui(kritaui_LIB_SRCS
widgets/KoFillConfigWidget.ui
widgets/KoStrokeConfigWidget.ui
forms/wdgdlgpngimport.ui
forms/wdgfullscreensettings.ui
forms/wdgautogradient.ui
forms/wdggeneralsettings.ui
forms/wdgperformancesettings.ui
forms/wdggenerators.ui
forms/wdgbookmarkedconfigurationseditor.ui
forms/wdgapplyprofile.ui
forms/wdgcustompattern.ui
forms/wdglayerproperties.ui
forms/wdgcolorsettings.ui
forms/wdgtabletsettings.ui
forms/wdgcolorspaceselector.ui
forms/wdgcolorspaceselectoradvanced.ui
forms/wdgdisplaysettings.ui
forms/kis_previewwidgetbase.ui
forms/kis_matrix_widget.ui
forms/wdgselectionoptions.ui
forms/wdggeometryoptions.ui
forms/wdgnewimage.ui
forms/wdgimageproperties.ui
forms/wdgmaskfromselection.ui
forms/wdgmasksource.ui
forms/wdgfilterdialog.ui
forms/wdgmetadatamergestrategychooser.ui
forms/wdgpaintoppresets.ui
forms/wdgpaintopsettings.ui
forms/wdgdlggeneratorlayer.ui
forms/wdgdlgfilelayer.ui
forms/wdgfilterselector.ui
forms/wdgfilternodecreation.ui
forms/wdgpaintactioneditor.ui
forms/wdgmultipliersdoublesliderspinbox.ui
forms/wdgnodequerypatheditor.ui
forms/wdgpresetselectorstrip.ui
forms/wdgsavebrushpreset.ui
forms/wdgpreseticonlibrary.ui
forms/wdgdlgblacklistcleanup.ui
forms/wdgrectangleconstraints.ui
forms/wdgimportimagesequence.ui
forms/wdgstrokeselectionproperties.ui
forms/KisDetailsPaneBase.ui
forms/KisOpenPaneBase.ui
forms/wdgstopgradienteditor.ui
forms/wdgsessionmanager.ui
forms/wdgnewwindowlayout.ui
brushhud/kis_dlg_brush_hud_config.ui
forms/wdgdlginternalcolorselector.ui
dialogs/kis_delayed_save_dialog.ui
input/config/kis_input_configuration_page.ui
input/config/kis_edit_profiles_dialog.ui
input/config/kis_input_configuration_page_item.ui
input/config/kis_mouse_input_editor.ui
input/config/kis_wheel_input_editor.ui
input/config/kis_key_input_editor.ui
layerstyles/wdgBevelAndEmboss.ui
layerstyles/wdgblendingoptions.ui
layerstyles/WdgColorOverlay.ui
layerstyles/wdgContour.ui
layerstyles/wdgdropshadow.ui
layerstyles/WdgGradientOverlay.ui
layerstyles/wdgInnerGlow.ui
layerstyles/wdglayerstyles.ui
layerstyles/WdgPatternOverlay.ui
layerstyles/WdgSatin.ui
layerstyles/WdgStroke.ui
layerstyles/wdgstylesselector.ui
layerstyles/wdgTexture.ui
wdgsplash.ui
input/wintab/kis_screen_size_choice_dialog.ui
)
QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h)
add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} )
generate_export_header(kritaui BASE_NAME kritaui)
target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network
kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES}
)
if (HAVE_QT_MULTIMEDIA)
target_link_libraries(kritaui Qt5::Multimedia)
endif()
if (HAVE_KIO)
target_link_libraries(kritaui KF5::KIOCore)
endif()
if (NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${X11_X11_LIB}
${X11_Xinput_LIB}
${XCB_LIBRARIES})
endif()
if(APPLE)
target_link_libraries(kritaui ${FOUNDATION_LIBRARY})
target_link_libraries(kritaui ${APPKIT_LIBRARY})
endif ()
target_link_libraries(kritaui ${OPENEXR_LIBRARIES})
# Add VSync disable workaround
if(NOT WIN32 AND NOT APPLE)
target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras)
endif()
if(X11_FOUND)
target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES})
endif()
target_include_directories(kritaui
PUBLIC
$
$
$
$
$
$
$
)
set_target_properties(kritaui
PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION}
)
install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS})
if (APPLE)
install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita)
endif ()
diff --git a/libs/ui/kis_view_plugin.cpp b/libs/ui/KisActionPlugin.cpp
similarity index 51%
rename from libs/ui/kis_view_plugin.cpp
rename to libs/ui/KisActionPlugin.cpp
index 3351391c30..3968b9ffe1 100644
--- a/libs/ui/kis_view_plugin.cpp
+++ b/libs/ui/KisActionPlugin.cpp
@@ -1,63 +1,68 @@
/*
* Copyright (c) 2013 Sven Langkamp
*
* 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_view_plugin.h"
+#include "KisActionPlugin.h"
#include "KisViewManager.h"
#include "kis_action_manager.h"
#include "operations/kis_operation.h"
-KisViewPlugin::KisViewPlugin(QObject* parent)
+KisActionPlugin::KisActionPlugin(QObject* parent)
: QObject(parent)
{
- m_view = qobject_cast(parent);
- Q_ASSERT(m_view);
+ m_viewManager = qobject_cast(parent);
+ Q_ASSERT(m_viewManager);
}
-KisViewPlugin::~KisViewPlugin()
+KisActionPlugin::~KisActionPlugin()
{
}
-void KisViewPlugin::addAction(const QString& name, KisAction* action)
+void KisActionPlugin::addAction(const QString& name, KisAction* action)
{
- if (m_view) {
- m_view->actionManager()->addAction(name, action);
+ if (m_viewManager) {
+ m_viewManager->actionManager()->addAction(name, action);
}
}
-KisAction* KisViewPlugin::createAction(const QString& name)
+KisAction* KisActionPlugin::createAction(const QString& name)
{
- if (m_view) {
- return m_view->actionManager()->createAction(name);
+ if (m_viewManager) {
+ return m_viewManager->actionManager()->createAction(name);
}
return 0;
}
-void KisViewPlugin::addUIFactory(KisOperationUIFactory* factory)
+void KisActionPlugin::addUIFactory(KisOperationUIFactory* factory)
{
- if (m_view) {
- m_view->actionManager()->registerOperationUIFactory(factory);
+ if (m_viewManager) {
+ m_viewManager->actionManager()->registerOperationUIFactory(factory);
}
}
-void KisViewPlugin::addOperation(KisOperation* operation)
+void KisActionPlugin::addOperation(KisOperation* operation)
{
- if (m_view) {
- m_view->actionManager()->registerOperation(operation);
+ if (m_viewManager) {
+ m_viewManager->actionManager()->registerOperation(operation);
}
}
+
+QPointer KisActionPlugin::viewManager() const
+{
+ return m_viewManager;
+}
diff --git a/libs/ui/kis_view_plugin.h b/libs/ui/KisActionPlugin.h
similarity index 70%
rename from libs/ui/kis_view_plugin.h
rename to libs/ui/KisActionPlugin.h
index bdaa6041c8..43e551bc6d 100644
--- a/libs/ui/kis_view_plugin.h
+++ b/libs/ui/KisActionPlugin.h
@@ -1,60 +1,63 @@
/*
* Copyright (c) 2013 Sven Langkamp
*
* 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_VIEW_PLUGIN_H
#define KIS_VIEW_PLUGIN_H
#include
#include
#include
class KisOperation;
class KisOperationUIFactory;
class KisAction;
class KisViewManager;
/**
- * KisViewPlugin is the base for plugins which add actions to the view
+ * KisActionPlugin is the base for plugins which add actions to the main window
*/
-class KRITAUI_EXPORT KisViewPlugin : public QObject
+class KRITAUI_EXPORT KisActionPlugin : public QObject
{
Q_OBJECT
public:
- KisViewPlugin(QObject* parent = 0);
- ~KisViewPlugin() override;
+ KisActionPlugin(QObject *parent = 0);
+ ~KisActionPlugin() override;
protected:
/**
* Registers a KisAction to the UI and action manager.
* @param name - title of the action in the krita4.xmlgui file
* @param action the action that should be added
*/
- void addAction(const QString& name, KisAction* action);
+ void addAction(const QString& name, KisAction *action);
- KisAction* createAction(const QString& name);
+ KisAction *createAction(const QString &name);
- void addUIFactory(KisOperationUIFactory* factory);
+ void addUIFactory(KisOperationUIFactory *factory);
- void addOperation(KisOperation* operation);
+ void addOperation(KisOperation *operation);
- QPointer m_view;
+ QPointer viewManager() const;
+
+private:
+ QPointer m_viewManager;
};
#endif // KIS_VIEW_PLUGIN_H
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 203675776a..82a5afcade 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2606 +1,2609 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2000-2006 David Faure
Copyright (C) 2007, 2009 Thomas zander
Copyright (C) 2010 Benjamin Port