Commit 664f3c58 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Merge branch 'krita-chili-kazakov' into krita-einspline-cpp-kazakov

Conflicts:
	krita/image/CMakeLists.txt
parents c29c1d1b 4ba229df
......@@ -24,16 +24,18 @@
# the needed internal build-time requirements. Any other product or feature
# listed as requirement must have been defined before.
# A "productset" is a selection of products which should be build together. The
# products can be either essential or optional to the set. If essential
# (NEEDED), the whole productset will not be build if a product is missing
# another internal or external dependency. If optional (WANTED), the rest of the
# set will still be build in that case.
# The products to include in a set can be listed directly or indirectly: they
# can be named themselves, or another productset can be included in a set, whose
# products will then be part of the first set as well.
# Products and productsets can be listed as dependencies in multiple product
# sets. As with dependencies for products, they must have been defined before.
# A "productset" is a selection of products and features which should be build
# together. The products and features can be either essential or optional to the
# set. If essential (REQUIRES), the whole productset will not be build if a
# product or feature is missing another internal or external dependency. If
# optional (OPTIONAL), the rest of the set will still be build in that case.
# The products and features to include in a set can be listed directly or
# indirectly: they can be named explicitely, but also by including other
# productsets in a set, whose products and features will then be part of the
# first set as well.
# Products, features and productsets can be listed as dependencies in multiple
# product sets. As with dependencies for products or features, they must have
# been defined before.
# Products, features and product sets are in the same namespace, so a given
# identifier can be only used either for a product or for a feature or for a
......@@ -670,6 +672,14 @@ calligra_define_productset(OKULAR "Okular generators"
# How to add another productset?
# ==============================
#
# There are two possible places to put a productset definition. The first is to
# add it to this file, which should be done for more generic sets that are
# useful for many people. The second is a file of its own, in the directory
# "cmake/productsets", which should be done for more special ones or for those
# which should not be added to the repository.
# The file must be named with the name of the productset in lowercase and have
# the extension ".cmake".
#
# 1. Define the productset by a call of calligra_define_productset,
# e.g.
#
......@@ -690,16 +700,9 @@ calligra_define_productset(OKULAR "Okular generators"
# 3. Add the productset to all product sets which have this product set as
# REQUIRED or OPTIONAL dependency.
#
# The definition could be added to this file, which should be done for more
# generic sets. Or it can be put into a file of its own, in the directory
# cmake/productsets, which should be done for more special ones or for those
# which should not be added to the repository.
# The file must be named with the name of the productset in lowercase and have
# the extension ".cmake".
#
# Example:
# Example for a file-based productset definition:
# You want a productset "MYWORDS". For that you add a file named
# "mywords.cmake" into the directory cmake/productsets, with the content:
# "mywords.cmake" into the directory "cmake/productsets", with the content:
# --- 8< ---
# calligra_define_productset(MYWORDS "My Words"
# REQUIRES
......
This directory stores additional productsets.
Please read the explanation in the toplevel file "CalligraProducts.cmake".
Please read the explanation in the toplevel file "CalligraProducts.cmake",
secion "How to add another productset?" at the end.
......@@ -72,8 +72,8 @@ void KisGradientBenchmark::benchmarkGradient()
fillPainter.setOpacity(OPACITY_OPAQUE_U8);
// default
fillPainter.setCompositeOp(COMPOSITE_OVER);
fillPainter.paintGradient(QPointF(0,0), QPointF(3000,3000), KisGradientPainter::GradientShapeBiLinear, KisGradientPainter::GradientRepeatNone, true, false, 0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT);
fillPainter.setGradientShape(KisGradientPainter::GradientShapeBiLinear);
fillPainter.paintGradient(QPointF(0,0), QPointF(3000,3000), KisGradientPainter::GradientRepeatNone, true, false, 0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT);
fillPainter.deleteTransaction();
}
......
......@@ -135,7 +135,15 @@ set(kritaimage_LIB_SRCS
kis_fill_painter.cc
kis_filter_mask.cpp
kis_filter_strategy.cc
kis_transform_mask.cpp
kis_transform_mask_params_interface.cpp
kis_recalculate_transform_mask_job.cpp
kis_transform_mask_params_factory_registry.cpp
kis_safe_transform.cpp
kis_gradient_painter.cc
kis_gradient_shape_strategy.cpp
kis_cached_gradient_shape_strategy.cpp
kis_polygonal_gradient_shape_strategy.cpp
kis_iterator_ng.cpp
kis_async_merger.cpp
kis_merge_walker.cc
......@@ -209,6 +217,7 @@ set(kritaimage_LIB_SRCS
kis_liquify_transform_worker.cpp
kis_green_coordinates_math.cpp
kis_algebra_2d.cpp
kis_dom_utils.cpp
kis_transparency_mask.cc
kis_undo_store.cpp
kis_undo_stores.cpp
......@@ -264,7 +273,7 @@ set(einspline_SRCS
bsplines/einspline/nugrid.cpp
)
#SET_SOURCE_FILES_PROPERTIES(${einspline_SRCS} PROPERTIES COMPILE_FLAGS -std=c99)
SET_SOURCE_FILES_PROPERTIES(${einspline_SRCS} PROPERTIES COMPILE_FLAGS -std=c99)
kde4_add_library(kritaimage SHARED ${kritaimage_LIB_SRCS} ${einspline_SRCS})
......@@ -288,6 +297,12 @@ if(HAVE_VC)
target_link_libraries(kritaimage ${Vc_LIBRARIES})
endif(HAVE_VC)
if (NOT GSL_FOUND)
message (WARNING "KRITA WARNING! No GNU Scientific Library was found! Krita's Shaped Gradients might be non-normalized! Please install GSL library.")
else (NOT GSL_FOUND)
target_link_libraries(kritaimage ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES})
endif (NOT GSL_FOUND)
set_target_properties(kritaimage PROPERTIES
VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION}
)
......
......@@ -51,6 +51,7 @@ void KisImageSetResolutionCommand::redo()
#include "kis_external_layer_iface.h"
#include "kis_transparency_mask.h"
#include "kis_filter_mask.h"
#include "kis_transform_mask.h"
#include "kis_selection_mask.h"
#include "kis_selection.h"
......@@ -67,6 +68,7 @@ public:
void visit(KisGeneratorLayer *layer, KisUndoAdapter*) { layer->internalSelection()->updateProjection(); }
void visit(KisExternalLayer *layer, KisUndoAdapter*) { layer->resetCache(); }
void visit(KisFilterMask *mask, KisUndoAdapter*) { mask->selection()->updateProjection(); }
void visit(KisTransformMask *mask, KisUndoAdapter*) { KIS_ASSERT_RECOVER_NOOP(!mask->selection()); }
void visit(KisTransparencyMask *mask, KisUndoAdapter*) { mask->selection()->updateProjection(); }
void visit(KisSelectionMask *mask, KisUndoAdapter*) { mask->selection()->updateProjection(); }
};
......
......@@ -124,4 +124,12 @@ QPainterPath smallArrow()
return p;
}
QRect blowRect(const QRect &rect, qreal coeff)
{
int w = rect.width() * coeff;
int h = rect.height() * coeff;
return rect.adjusted(-w, -h, w, h);
}
}
......@@ -81,10 +81,17 @@ Point normalize(const Point &a)
*/
template <typename T>
T signPZ(T x) {
const T zeroValue(0);
return x >= T(0) ? T(1) : T(-1);
}
/**
* Usual sign() function with zero returning zero
*/
template <typename T>
T signZZ(T x) {
return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
}
template <class T>
T leftUnitNormal(const T &a)
{
......@@ -209,6 +216,12 @@ inline Point clampPoint(Point pt, const Rect &bounds)
QPainterPath KRITAIMAGE_EXPORT smallArrow();
/**
* Multiply width and height of \p rect by \p coeff keeping the
* center of the rectangle pinned
*/
QRect KRITAIMAGE_EXPORT blowRect(const QRect &rect, qreal coeff);
}
#endif /* __KIS_ALGEBRA_2D_H */
......@@ -151,6 +151,18 @@ public:
QRegion prepareRegion(srcRect);
prepareRegion -= m_cropRect;
QStack<QRect> applyRects;
bool rectVariesFlag;
/**
* If a clone has complicated masks, we should prepare additional
* source area to ensure the rect is prepared.
*/
QRect needRectOnSource = layer->needRectOnSourceForMasks(srcRect);
if (!needRectOnSource.isEmpty()) {
prepareRegion += needRectOnSource;
}
foreach(const QRect &rect, prepareRegion.rects()) {
walker.collectRects(srcLayer, rect);
merger.startMerge(walker, false);
......@@ -165,6 +177,9 @@ public:
bool visit(KisFilterMask*) {
return true;
}
bool visit(KisTransformMask*) {
return true;
}
bool visit(KisTransparencyMask*) {
return true;
}
......@@ -209,7 +224,7 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
m_currentProjection,
walker.cropRect());
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
continue;
}
......@@ -225,18 +240,18 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
if(item.m_position & KisMergeWalker::N_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentNode, applyRect);
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
}
else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) {
DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentNode, applyRect);
if(dependOnLowerNodes(currentNode)) {
currentNode->accept(originalVisitor);
currentNode->updateProjection(applyRect);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
}
}
else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) {
DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentNode, applyRect);
currentNode->updateProjection(applyRect);
currentNode->updateProjection(applyRect, KisMergeWalker::convertPositionToFilthy(item.m_position));
}
else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ {
DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentNode, applyRect);
......
......@@ -58,7 +58,20 @@ public:
};
#define GRAPH_POSITION_MASK 0x07
#define POSITION_TO_FILTHY_MASK 0xF8
static inline KisNode::PositionToFilthy convertPositionToFilthy(NodePosition position) {
static const int positionToFilthyMask =
N_ABOVE_FILTHY |
N_FILTHY_PROJECTION |
N_FILTHY |
N_BELOW_FILTHY;
qint32 positionToFilthy = position & N_EXTRA ? N_FILTHY : position & positionToFilthyMask;
// We do not use N_FILTHY_ORIGINAL yet, so...
Q_ASSERT(positionToFilthy);
return static_cast<KisNode::PositionToFilthy>(positionToFilthy);
}
struct CloneNotification {
CloneNotification() {}
......@@ -189,13 +202,6 @@ protected:
virtual void startTrip(KisNodeSP startWith) = 0;
protected:
static inline KisNode::PositionToFilthy getPositionToFilthy(qint32 position) {
qint32 positionToFilthy = position & POSITION_TO_FILTHY_MASK;
// We do not use N_FILTHY_ORIGINAL yet, so...
Q_ASSERT(!(positionToFilthy & N_FILTHY_ORIGINAL));
return static_cast<KisNode::PositionToFilthy>(positionToFilthy);
}
static inline qint32 getGraphPosition(qint32 position) {
return position & GRAPH_POSITION_MASK;
......@@ -270,7 +276,7 @@ protected:
if(!isLayer(node)) return;
QRect currentChangeRect = node->changeRect(m_resultChangeRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
currentChangeRect = cropThisRect(currentChangeRect);
if(!m_changeRectVaries)
......@@ -279,7 +285,7 @@ protected:
m_resultChangeRect = currentChangeRect;
m_resultUncroppedChangeRect = node->changeRect(m_resultUncroppedChangeRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
registerCloneNotification(node, position);
}
......@@ -320,10 +326,10 @@ protected:
//else /* Why push empty rect? */;
m_resultAccessRect |= node->accessRect(m_lastNeedRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
m_lastNeedRect = node->needRect(m_lastNeedRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
m_childNeedRect = m_lastNeedRect;
}
......@@ -332,10 +338,10 @@ protected:
pushJob(node, position, m_lastNeedRect);
m_resultAccessRect |= node->accessRect(m_lastNeedRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
m_lastNeedRect = node->needRect(m_lastNeedRect,
getPositionToFilthy(position));
convertPositionToFilthy(position));
m_lastNeedRect = cropThisRect(m_lastNeedRect);
}
}
......
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_cached_gradient_shape_strategy.h"
#include <QRect>
#include "bsplines/kis_bspline_2d.h"
#include <cmath>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace KisBSplines;
struct KisCachedGradientShapeStrategy::Private
{
QRect rc;
qreal xStep;
qreal yStep;
QScopedPointer<KisGradientShapeStrategy> baseStrategy;
QScopedPointer<KisBSpline2D> spline;
};
KisCachedGradientShapeStrategy::KisCachedGradientShapeStrategy(const QRect &rc,
qreal xStep,
qreal yStep,
KisGradientShapeStrategy *baseStrategy)
: KisGradientShapeStrategy(),
m_d(new Private())
{
m_d->rc = rc;
m_d->xStep = xStep;
m_d->yStep = yStep;
m_d->baseStrategy.reset(baseStrategy);
qreal xStart = rc.x();
qreal yStart = rc.y();
qreal xEnd = rc.x() + rc.width();
qreal yEnd = rc.y() + rc.height();
int numSamplesX = std::ceil(qreal(rc.width()) / xStep);
int numSamplesY = std::ceil(qreal(rc.height()) / yStep);
m_d->spline.reset(new KisBSpline2D(xStart, xEnd, numSamplesX, Natural,
yStart, yEnd, numSamplesY, Natural));
boost::function<qreal(qreal, qreal)> valueOp =
boost::bind(&KisGradientShapeStrategy::valueAt, m_d->baseStrategy.data(), _1, _2);
m_d->spline->initializeSpline(valueOp);
}
KisCachedGradientShapeStrategy::~KisCachedGradientShapeStrategy()
{
}
double KisCachedGradientShapeStrategy::valueAt(double x, double y) const
{
return m_d->spline->value(x, y);
}
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_CACHED_GRADIENT_SHAPE_STRATEGY_H
#define __KIS_CACHED_GRADIENT_SHAPE_STRATEGY_H
#include "kis_gradient_shape_strategy.h"
#include "krita_export.h"
#include <QScopedPointer>
class QRect;
class KRITAIMAGE_EXPORT KisCachedGradientShapeStrategy : public KisGradientShapeStrategy
{
public:
KisCachedGradientShapeStrategy(const QRect &rc, qreal xStep, qreal yStep, KisGradientShapeStrategy *baseStrategy);
~KisCachedGradientShapeStrategy();
double valueAt(double x, double y) const;
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_CACHED_GRADIENT_SHAPE_STRATEGY_H */
/*
* Copyright (c) 2014 Dmitry Kazakov <dimula73@gmail.com>
*
* 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_CACHED_PAINT_DEVICE_H
#define __KIS_CACHED_PAINT_DEVICE_H
#include "tiles3/kis_lockless_stack.h"
class KisCachedPaintDevice
{
public:
KisPaintDeviceSP getDevice(KisPaintDeviceSP prototype) {
KisPaintDeviceSP device;
if(!m_stack.pop(device)) {
device = new KisPaintDevice(prototype->colorSpace());
}
device->prepareClone(prototype);
return device;
}
void putDevice(KisPaintDeviceSP device) {
device->clear();
m_stack.push(device);
}
private:
KisLocklessStack<KisPaintDeviceSP> m_stack;
};
#endif /* __KIS_CACHED_PAINT_DEVICE_H */
......@@ -92,6 +92,9 @@ public:
bool visit(KisFilterMask*) {
return true;
}
bool visit(KisTransformMask*) {
return true;
}
bool visit(KisTransparencyMask*) {
return true;
}
......
......@@ -34,6 +34,9 @@
#include "kis_clone_info.h"
#include "kis_paint_layer.h"
#include <QStack>
#include <kis_effect_mask.h>
struct KisCloneLayer::Private
{
......@@ -157,6 +160,28 @@ void KisCloneLayer::notifyParentVisibilityChanged(bool value)
KisLayer::notifyParentVisibilityChanged(value);
}
QRect KisCloneLayer::needRectOnSourceForMasks(const QRect &rc) const
{
QStack<QRect> applyRects_unused;
bool rectVariesFlag;
QList<KisEffectMaskSP> effectMasks = this->effectMasks();
if (effectMasks.isEmpty()) return QRect();
QRect needRect = this->masksNeedRect(effectMasks,
rc,
applyRects_unused,
rectVariesFlag);
if (needRect.isEmpty() ||
(!rectVariesFlag && needRect == rc)) {
return QRect();
}
return needRect;
}
qint32 KisCloneLayer::x() const
{
return m_d->x;
......@@ -196,8 +221,17 @@ QRect KisCloneLayer::accessRect(const QRect &rect, PositionToFilthy pos) const
{
QRect resultRect = rect;
if(pos & (N_FILTHY_PROJECTION | N_FILTHY) && (m_d->x || m_d->y)) {
resultRect |= rect.translated(-m_d->x, -m_d->y);
if(pos & (N_FILTHY_PROJECTION | N_FILTHY)) {
if (m_d->x || m_d->y) {
resultRect |= rect.translated(-m_d->x, -m_d->y);
}
/**
* KisUpdateOriginalVisitor will try to recalculate some area
* on the clone's source, so this extra rectangle should also
* be taken into account
*/
resultRect |= needRectOnSourceForMasks(rect);
}
return resultRect;
......