Krita Source Code Documentation
Loading...
Searching...
No Matches
KisScreentoneGeneratorFunctionSampler.h
Go to the documentation of this file.
1/*
2 * KDE. Krita Project.
3 *
4 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#ifndef KISSCREENTONEGENERATORFUNCTIONSAMPLER_H
10#define KISSCREENTONEGENERATORFUNCTIONSAMPLER_H
11
12#include <QtGlobal>
13#include <QTransform>
14
16
17template <typename Function>
19{
20public:
24
26 const Function &the_function)
27 : m_function(the_function)
28 {
29 // Get transformation parameters
30 qreal sizeX, sizeY;
31 if (config->sizeMode() == KisScreentoneSizeMode_PixelBased) {
32 const bool constrainSize = config->constrainSize();
33 sizeX = config->sizeX();
34 // Ensure that the size y component is equal to the x component if keepSizeSquare is true
35 sizeY = constrainSize ? sizeX : config->sizeY();
36 } else {
37 const qreal resolution = config->resolution();
38 const bool constrainFrequency = config->constrainFrequency();
39 const qreal frequencyX = config->frequencyX();
40 // Ensure that the frequency y component is equal to the x component if constrainFrequency is true
41 const qreal frequencyY = constrainFrequency ? frequencyX : config->frequencyY();
42 sizeX = qMax(1.0, resolution / frequencyX);
43 sizeY = qMax(1.0, resolution / frequencyY);
44 }
45 const qreal positionX = config->positionX();
46 const qreal positionY = config->positionY();
47 const qreal shearX = config->shearX();
48 const qreal shearY = config->shearY();
49 const qreal rotation = config->rotation();
50
51 // Get final transformation
52 QTransform t;
53 if (config->alignToPixelGrid()) {
54 t.rotate(-rotation);
55 t.scale(sizeX, sizeY);
56 t.shear(-shearX, -shearY);
57 const QSizeF macrocellSize(
58 static_cast<qreal>(config->alignToPixelGridX()),
59 static_cast<qreal>(config->alignToPixelGridY())
60 );
61 // u1 is the unaligned vector that goes from the origin to the top-right
62 // corner of the macrocell. v1 is the aligned version
63 // u2 is the unaligned vector that goes from the origin to the bottom-left
64 // corner of the macrocell. v2 is the aligned version
65 const QPointF u1 = t.map(QPointF(macrocellSize.width(), 0.0));
66 const QPointF u2 = t.map(QPointF(0.0, macrocellSize.height()));
67 QPointF v1(qRound(u1.x()), qRound(u1.y()));
68 QPointF v2(qRound(u2.x()), qRound(u2.y()));
69 // If the following condition is met, that means that the screen is
70 // transformed in such a way that the cell corners are colinear so we move
71 // v1 or v2 to a neighbor position
72 if (qFuzzyCompare(v1.y() * v2.x(), v2.y() * v1.x()) &&
73 !qFuzzyIsNull(v1.x() * v2.x() + v1.y() * v2.y())) {
74 // Choose point to move based on distance from non aligned point to
75 // aligned point
76 const qreal dist1 = kisSquareDistance(u1, v1);
77 const qreal dist2 = kisSquareDistance(u2, v2);
78 const QPointF *p_u = dist1 > dist2 ? &u1 : &u2;
79 QPointF *p_v = dist1 > dist2 ? &v1 : &v2;
80 // Then we get the closest pixel aligned point to the current,
81 // colinear, point
82 QPair<int, qreal> dists[4]{
83 {1, kisSquareDistance(*p_u, *p_v + QPointF(0.0, -1.0))},
84 {2, kisSquareDistance(*p_u, *p_v + QPointF(1.0, 0.0))},
85 {3, kisSquareDistance(*p_u, *p_v + QPointF(0.0, 1.0))},
86 {4, kisSquareDistance(*p_u, *p_v + QPointF(-1.0, 0.0))}
87 };
88 std::sort(
89 std::begin(dists), std::end(dists),
90 [](const QPair<int, qreal> &a, const QPair<int, qreal> &b)
91 {
92 return a.second < b.second;
93 }
94 );
95 // Move the point
96 if (dists[0].first == 1) {
97 p_v->setY(p_v->y() - 1.0);
98 } else if (dists[0].first == 2) {
99 p_v->setX(p_v->x() + 1.0);
100 } else if (dists[0].first == 3) {
101 p_v->setY(p_v->y() + 1.0);
102 } else {
103 p_v->setX(p_v->x() - 1.0);
104 }
105 }
106 QPolygonF quad;
107 quad.append(QPointF(0, 0));
108 quad.append(v1 / macrocellSize.width());
109 quad.append(v1 / macrocellSize.width() + v2 / macrocellSize.height());
110 quad.append(v2 / macrocellSize.height());
111 QTransform::quadToSquare(quad, t);
112 t.translate(qRound(positionX), qRound(positionY));
113 } else {
114 t.shear(shearX, shearY);
115 t.scale(qFuzzyIsNull(sizeX) ? 0.0 : 1.0 / sizeX, qFuzzyIsNull(sizeY) ? 0.0 : 1.0 / sizeY);
116 t.rotate(rotation);
117 t.translate(positionX, positionY);
118 }
119
121 }
122
123 qreal operator()(qreal x, qreal y) const
124 {
125 // Get the coordinates in screen
126 qreal xx, yy;
127 m_imageToScreenTransform.map(x, y, &xx, &yy);
128 // Get the value
129 return m_function(xx, yy);
130 }
131
132private:
133 Function m_function;
135};
136
137#endif
KisScreentoneGeneratorFunctionSampler(const KisScreentoneGeneratorConfigurationSP config, const Function &the_function)
KisScreentoneGeneratorFunctionSampler(const KisScreentoneGeneratorConfigurationSP config)
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194