20 : srcBounds(_srcBounds),
22 pixelPrecision(_pixelPrecision)
39 template <
class ProcessOp>
44 template <
class ProcessOp>
50 template <
class ProcessOp>
61 : m_d(new
Private(srcBounds, progress, pixelPrecision))
70 : m_d(new
Private(*rhs.m_d.data()))
81 m_d->srcBounds == other.
m_d->srcBounds &&
82 m_d->pixelPrecision == other.
m_d->pixelPrecision &&
83 m_d->gridSize == other.
m_d->gridSize &&
84 m_d->originalPoints.size() == other.
m_d->originalPoints.size() &&
85 m_d->transformedPoints.size() == other.
m_d->transformedPoints.size();
87 if (!result)
return false;
89 const qreal
eps = 1e-6;
100 const qreal
eps = 1e-6;
111 return m_d->gridSize;
116 return m_d->originalPoints;
121 return m_d->transformedPoints;
129 int prevCol,
int prevRow,
130 int colIndex,
int rowIndex) {
137 QPointF pt(col, row);
148void KisLiquifyTransformWorker::Private::preparePoints()
156 const int numPoints = pointsOp.
m_points.size();
161 transformedPoints = pointsOp.
m_points;
171 m_d->transformedPoints.size());
173 for (; it != end; ++it, ++refIt) {
184 for (; it != end; ++it) {
193 const qreal maxDistCoeff = 3.0;
194 const qreal maxDist = maxDistCoeff * sigma;
195 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
196 2 * maxDist, 2 * maxDist);
203 m_d->transformedPoints.size());
205 for (; it != end; ++it, ++refIt) {
206 if (!clipRect.contains(*it))
continue;
208 QPointF diff = *it - base;
210 if (dist > maxDist)
continue;
212 qreal lambda = exp(-0.5 *
pow2(dist / sigma));
214 *it = *refIt * lambda + *it * (1.0 - lambda);
218template <
class ProcessOp>
219void KisLiquifyTransformWorker::Private::
220processTransformedPixelsBuildUp(ProcessOp op,
224 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
225 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
226 2 * maxDist, 2 * maxDist);
231 for (; it != end; ++it) {
232 if (!clipRect.contains(*it))
continue;
234 QPointF diff = *it - base;
236 if (dist > maxDist)
continue;
238 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
239 *it = op(*it, base, diff, lambda);
243template <
class ProcessOp>
244void KisLiquifyTransformWorker::Private::
245processTransformedPixelsWash(ProcessOp op,
250 const qreal maxDist = ProcessOp::maxDistCoeff * sigma;
251 QRectF clipRect(base.x() - maxDist, base.y() - maxDist,
252 2 * maxDist, 2 * maxDist);
259 transformedPoints.size());
261 for (; it != end; ++it, ++refIt) {
262 if (!clipRect.contains(*it))
continue;
264 QPointF diff = *refIt - base;
266 if (dist > maxDist)
continue;
268 const qreal lambda = exp(-0.5 *
pow2(dist / sigma));
269 QPointF dstPt = op(*refIt, base, diff, lambda);
272 *it = (1.0 - flow) * (*it) + flow * dstPt;
277template <
class ProcessOp>
278void KisLiquifyTransformWorker::Private::
279processTransformedPixels(ProcessOp op,
286 processTransformedPixelsWash(op, base, sigma, flow);
288 processTransformedPixelsBuildUp(op, base, sigma);
324 return base + (1.0 +
m_scale * lambda) * diff;
345 const qreal angle =
m_angle * lambda;
346 const qreal sinA = std::sin(angle);
347 const qreal cosA = std::cos(angle);
349 qreal x = cosA * diff.x() + sinA * diff.y();
350 qreal y = -sinA * diff.x() + cosA * diff.y();
352 return base + QPointF(x, y);
363 const QPointF &offset,
369 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
379 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
389 m_d->processTransformedPixels(op, base, sigma, useWashMode, flow);
402 iterateThroughGrid<AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
405 m_d->transformedPoints);
410 const qreal margin = 0.05;
416 const int maxSamplePoints = 200;
417 const int minStep = 3;
418 const int step = qMax(minStep,
m_d->transformedPoints.size() / maxSamplePoints);
422 for (
auto it =
m_d->transformedPoints.constBegin(); it !=
m_d->transformedPoints.constEnd(); ++it) {
423 samplePoints << it->toPoint();
440 m_d->srcBounds = t.mapRect(
m_d->srcBounds);
442 for (
auto it =
m_d->originalPoints.begin(); it !=
m_d->originalPoints.end(); ++it) {
445 for (
auto it =
m_d->transformedPoints.begin(); it !=
m_d->transformedPoints.end(); ++it) {
457 using namespace std::placeholders;
459 typedef QPointF (QTransform::*MapFuncType)(
const QPointF&)
const;
460 return std::bind(
static_cast<MapFuncType
>(&QTransform::map), &transform, _1);
464 const QPointF &srcImageOffset,
465 const QTransform &imageToThumbTransform,
485 std::transform(originalPointsLocal.begin(), originalPointsLocal.end(),
486 originalPointsLocal.begin(), mapFunc);
488 std::transform(transformedPointsLocal.begin(), transformedPointsLocal.end(),
489 transformedPointsLocal.begin(), mapFunc);
492 Q_FOREACH (
const QPointF &pt, transformedPointsLocal) {
496 const QRectF
srcBounds(srcImageOffset, srcImage.size());
499 QPointF dstQImageOffset = dstBounds.topLeft();
500 *newOffset = dstQImageOffset;
502 QRect dstBoundsI = dstBounds.toAlignedRect();
504 QImage dstImage(dstBoundsI.size(), srcImage.format());
513 transformedPointsLocal);
519 QDomDocument doc = e->ownerDocument();
520 QDomElement liqEl = doc.createElement(
"liquify_points");
521 e->appendChild(liqEl);
532 QDomElement liquifyEl;
553 warnKrita <<
"WARNING: Failed to load liquify worker from XML";
563 numPoints != worker->
m_d->originalPoints.size() ||
565 warnKrita <<
"WARNING: Inconsistent number of points!";
576 for (
int i = 0; i < numPoints; i++) {
const KoColorSpace * colorSpace() const
#define KIS_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_RETURN(cond)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Rect blowRect(const Rect &rect, qreal coeff)
QRect approximateRectFromPoints(const QVector< QPoint > &points)
void accumulateBounds(const Point &pt, Rect *bounds)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
bool findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages)
bool loadValue(const QDomElement &e, float *v)
QVector< QPointF > m_points
AllPointsFetcherOp(QRectF srcRect)
void processPoint(int col, int row, int prevCol, int prevRow, int colIndex, int rowIndex)
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
static const qreal maxDistCoeff
static const qreal maxDistCoeff
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
TranslateOp(const QPointF &offset)
QPointF operator()(const QPointF &pt, const QPointF &base, const QPointF &diff, qreal lambda)
static const qreal maxDistCoeff