Krita Source Code Documentation
Loading...
Searching...
No Matches
KisScreentoneGeneratorTemplate Class Reference

#include <KisScreentoneGeneratorTemplate.h>

Public Member Functions

const QTransform & imageToScreenTransform () const
 
 KisScreentoneGeneratorTemplate (const KisScreentoneGeneratorConfigurationSP config)
 
const QSize & macrocellSize () const
 
const QPoint & originOffset () const
 
const QPointF & screenPosition () const
 
const QTransform & screenToTemplateTransform () const
 
const QVector< qreal > & templateData () const
 
const QSize & templateSize () const
 
const QTransform & templateToScreenTransform () const
 
const QPointF & v1 () const
 
const QPointF & v2 () const
 

Private Member Functions

QVector< int > makeCellOrderList (int macrocellColumns, int macrocellRows) const
 
template<typename ScreentoneFunction >
void makeTemplate (const KisScreentoneGeneratorConfigurationSP config, ScreentoneFunction screentoneFunction)
 

Private Attributes

QTransform m_imageToScreenTransform
 
QSize m_macrocellSize
 
QPoint m_originOffset
 
QPointF m_screenPosition
 
QTransform m_screenToTemplateTransform
 
QVector< qreal > m_templateData
 
QSize m_templateSize
 
QTransform m_templateToScreenTransform
 
QPointF m_v1
 
QPointF m_v2
 

Detailed Description

Definition at line 18 of file KisScreentoneGeneratorTemplate.h.

Constructor & Destructor Documentation

◆ KisScreentoneGeneratorTemplate()

KisScreentoneGeneratorTemplate::KisScreentoneGeneratorTemplate ( const KisScreentoneGeneratorConfigurationSP config)

Definition at line 16 of file KisScreentoneGeneratorTemplate.cpp.

17{
18 const int pattern = config->pattern();
19 const int shape = config->shape();
20 const int interpolation = config->interpolation();
21
22 if (pattern == KisScreentonePatternType_Dots) {
24 if (interpolation == KisScreentoneInterpolationType_Linear) {
26 makeTemplate(config, screentoneFunction);
27 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
29 makeTemplate(config, screentoneFunction);
30 }
31 } else if (shape == KisScreentoneShapeType_EllipseDotsLegacy) {
32 if (interpolation == KisScreentoneInterpolationType_Linear) {
34 makeTemplate(config, screentoneFunction);
35 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
37 makeTemplate(config, screentoneFunction);
38 }
39 } else if (shape == KisScreentoneShapeType_EllipseDots) {
40 if (interpolation == KisScreentoneInterpolationType_Linear) {
42 makeTemplate(config, screentoneFunction);
43 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
45 makeTemplate(config, screentoneFunction);
46 }
47 } else if (shape == KisScreentoneShapeType_DiamondDots) {
49 makeTemplate(config, screentoneFunction);
50 } else if (shape == KisScreentoneShapeType_SquareDots) {
52 makeTemplate(config, screentoneFunction);
53 }
54 } else if (pattern == KisScreentonePatternType_Lines) {
56 if (interpolation == KisScreentoneInterpolationType_Linear) {
58 makeTemplate(config, screentoneFunction);
59 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
61 makeTemplate(config, screentoneFunction);
62 }
63 } else if (shape == KisScreentoneShapeType_SineWaveLines) {
64 if (interpolation == KisScreentoneInterpolationType_Linear) {
66 makeTemplate(config, screentoneFunction);
67 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
69 makeTemplate(config, screentoneFunction);
70 }
72 if (interpolation == KisScreentoneInterpolationType_Linear) {
74 makeTemplate(config, screentoneFunction);
75 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
77 makeTemplate(config, screentoneFunction);
78 }
79 } else if (shape == KisScreentoneShapeType_SawtoothWaveLines) {
80 if (interpolation == KisScreentoneInterpolationType_Linear) {
82 makeTemplate(config, screentoneFunction);
83 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
85 makeTemplate(config, screentoneFunction);
86 }
87 } else if (shape == KisScreentoneShapeType_CurtainsLines) {
88 if (interpolation == KisScreentoneInterpolationType_Linear) {
90 makeTemplate(config, screentoneFunction);
91 } else if (interpolation == KisScreentoneInterpolationType_Sinusoidal) {
93 makeTemplate(config, screentoneFunction);
94 }
95 }
96 }
97}
@ KisScreentoneShapeType_TriangularWaveLines
@ KisScreentoneInterpolationType_Sinusoidal
void makeTemplate(const KisScreentoneGeneratorConfigurationSP config, ScreentoneFunction screentoneFunction)

References KisScreentoneInterpolationType_Linear, KisScreentoneInterpolationType_Sinusoidal, KisScreentonePatternType_Dots, KisScreentonePatternType_Lines, KisScreentoneShapeType_CurtainsLines, KisScreentoneShapeType_DiamondDots, KisScreentoneShapeType_EllipseDots, KisScreentoneShapeType_EllipseDotsLegacy, KisScreentoneShapeType_RoundDots, KisScreentoneShapeType_SawtoothWaveLines, KisScreentoneShapeType_SineWaveLines, KisScreentoneShapeType_SquareDots, KisScreentoneShapeType_StraightLines, KisScreentoneShapeType_TriangularWaveLines, and makeTemplate().

Member Function Documentation

◆ imageToScreenTransform()

const QTransform & KisScreentoneGeneratorTemplate::imageToScreenTransform ( ) const
inline

◆ macrocellSize()

const QSize & KisScreentoneGeneratorTemplate::macrocellSize ( ) const
inline

◆ makeCellOrderList()

QVector< int > KisScreentoneGeneratorTemplate::makeCellOrderList ( int macrocellColumns,
int macrocellRows ) const
private

Definition at line 501 of file KisScreentoneGeneratorTemplate.cpp.

502{
503 if (macrocellColumns == 1 && macrocellRows == 1) {
504 return {0};
505 }
506
507 struct Candidate
508 {
509 int availableCellIndex;
510 int index;
511 int distanceSquared;
512 int averageDistance;
513 };
514
515 const int numberOfCells = macrocellColumns * macrocellRows;
516 QVector<int> processedCells;
517 QVector<int> availableCells;
518
519 // Insert the cell with index 0 in the first position
520 processedCells.push_back(0);
521 for (int i = 1; i < numberOfCells; ++i) {
522 availableCells.push_back(i);
523 }
524
525 while (availableCells.size() > 0) {
526 QVector<Candidate> candidates;
527 for (int i = 0; i < availableCells.size(); ++i) {
528 const int availableCellY = availableCells[i] / macrocellColumns;
529 const int availableCellX = availableCells[i] - availableCellY * macrocellColumns;
530 int averageDistanceToProcessedCells = 0;
531 int minimumDistanceToProcessedCells = std::numeric_limits<int>::max();
532 for (int j = 0; j < processedCells.size(); ++j) {
533 const int processedCellY = processedCells[j] / macrocellColumns;
534 const int processedCellX = processedCells[j] - processedCellY * macrocellColumns;
535
536 int distanceToCurrentProcessedCell = std::numeric_limits<int>::max();
537 for (int y = -1; y < 2; ++y) {
538 for (int x = -1; x < 2; ++x) {
539 const int xx = processedCellX + macrocellColumns * x;
540 const int yy = processedCellY + macrocellRows * y;
541 const int deltaX = availableCellX - xx;
542 const int deltaY = availableCellY - yy;
543 const int distanceSquared = deltaX * deltaX + deltaY * deltaY;
544 if (distanceSquared < distanceToCurrentProcessedCell) {
545 distanceToCurrentProcessedCell = distanceSquared;
546 }
547 }
548 }
549 if (distanceToCurrentProcessedCell < minimumDistanceToProcessedCells) {
550 minimumDistanceToProcessedCells = distanceToCurrentProcessedCell;
551 }
552 averageDistanceToProcessedCells += distanceToCurrentProcessedCell;
553 }
554 candidates.push_back({i, availableCells[i], minimumDistanceToProcessedCells, averageDistanceToProcessedCells});
555 }
556
557 // Find the best candidate
558 int bestCandidateIndex = 0;
559 for (int i = 0; i < candidates.size(); ++i) {
560 if (candidates[i].distanceSquared > candidates[bestCandidateIndex].distanceSquared ||
561 (candidates[i].distanceSquared == candidates[bestCandidateIndex].distanceSquared &&
562 candidates[i].averageDistance > candidates[bestCandidateIndex].averageDistance)) {
563 bestCandidateIndex = i;
564 }
565 }
566
567 processedCells.push_back(candidates[bestCandidateIndex].index);
568 availableCells.remove(candidates[bestCandidateIndex].availableCellIndex);
569 }
570
571 QVector<int> cellOrderList(numberOfCells);
572 for (int i = 1; i < numberOfCells; ++i) {
573 cellOrderList[processedCells[i]] = i;
574 }
575
576 return cellOrderList;
577}

◆ makeTemplate()

template<typename ScreentoneFunction >
void KisScreentoneGeneratorTemplate::makeTemplate ( const KisScreentoneGeneratorConfigurationSP config,
ScreentoneFunction screentoneFunction )
private

Definition at line 118 of file KisScreentoneGeneratorTemplate.cpp.

120{
121 // NOTE: In the following, the word "screen" is used to mean the dot screen
122 // of the screentone (the grid of dots/cells)
123
124 // Get transformation parameters
125 qreal sizeX, sizeY;
126 const int alignX = config->alignToPixelGrid() ? config->alignToPixelGridX() : 1;
127 const int alignY = config->alignToPixelGrid() ? config->alignToPixelGridY() : 1;
128 if (config->sizeMode() == KisScreentoneSizeMode_PixelBased) {
129 const bool constrainSize = config->constrainSize();
130 sizeX = config->sizeX();
131 // Ensure that the size y component is equal to the x component
132 // if keepSizeSquare is true
133 sizeY = constrainSize ? sizeX : config->sizeY();
134 } else {
135 const qreal resolution = config->resolution();
136 const bool constrainFrequency = config->constrainFrequency();
137 const qreal frequencyX = config->frequencyX();
138 // Ensure that the frequency y component is equal to the x component
139 // if constrainFrequency is true
140 const qreal frequencyY = constrainFrequency ? frequencyX : config->frequencyY();
141 sizeX = qMax(1.0, resolution / frequencyX);
142 sizeY = qMax(1.0, resolution / frequencyY);
143 }
144 const qreal positionX = config->alignToPixelGrid() ? qRound(config->positionX()) : config->positionX();
145 const qreal positionY = config->alignToPixelGrid() ? qRound(config->positionY()) : config->positionY();
146 m_screenPosition = QPointF(positionX, positionY);
147 const qreal shearX = config->shearX();
148 const qreal shearY = config->shearY();
149 const qreal rotation = config->rotation();
150 // Construct image<->screen transforms
151 m_imageToScreenTransform.shear(shearX, shearY);
152 m_imageToScreenTransform.scale(qFuzzyIsNull(sizeX) ? 0.0 : 1.0 / sizeX, qFuzzyIsNull(sizeY) ? 0.0 : 1.0 / sizeY);
153 m_imageToScreenTransform.rotate(rotation);
154 m_imageToScreenTransform.translate(positionX, positionY);
155 QTransform screenToImage;
156 screenToImage.rotate(-rotation);
157 screenToImage.scale(sizeX, sizeY);
158 screenToImage.shear(-shearX, -shearY);
159 // u1 is the unaligned vector that goes from the origin to the top-right
160 // corner of the macrocell. v1 is the aligned version
161 // u2 is the unaligned vector that goes from the origin to the bottom-left
162 // corner of the macrocell. v2 is the aligned version.
163 // At this point we assume the macrocell will have a minimum size given by
164 // the alignment
165 const QPointF u1 = screenToImage.map(QPointF(static_cast<qreal>(alignX), 0.0));
166 const QPointF u2 = screenToImage.map(QPointF(0.0, static_cast<qreal>(alignY)));
167 QPointF v1(qRound(u1.x()), qRound(u1.y()));
168 QPointF v2(qRound(u2.x()), qRound(u2.y()));
169 // If the following condition is met, that means that the screen is
170 // transformed in such a way that the cell corners are colinear so we move
171 // v1 or v2 to a neighbor position and give the cell some area
172 if (qFuzzyCompare(v1.y() * v2.x(), v2.y() * v1.x()) &&
173 !qFuzzyIsNull(v1.x() * v2.x() + v1.y() * v2.y())) {
174 // Choose point to move based on distance from non aligned point to
175 // aligned point
176 const qreal dist1 = kisSquareDistance(u1, v1);
177 const qreal dist2 = kisSquareDistance(u2, v2);
178 const QPointF *p_u = dist1 > dist2 ? &u1 : &u2;
179 QPointF *p_v = dist1 > dist2 ? &v1 : &v2;
180 // Then we get the closest pixel aligned point to the current, colinear,
181 // point
182 QPair<int, qreal> dists[4]{
183 {1, kisSquareDistance(*p_u, *p_v + QPointF(0.0, -1.0))},
184 {2, kisSquareDistance(*p_u, *p_v + QPointF(1.0, 0.0))},
185 {3, kisSquareDistance(*p_u, *p_v + QPointF(0.0, 1.0))},
186 {4, kisSquareDistance(*p_u, *p_v + QPointF(-1.0, 0.0))}
187 };
188 std::sort(
189 std::begin(dists), std::end(dists),
190 [](const QPair<int, qreal> &a, const QPair<int, qreal> &b)
191 {
192 return a.second < b.second;
193 }
194 );
195 // Move the point
196 if (dists[0].first == 1) {
197 p_v->setY(p_v->y() - 1.0);
198 } else if (dists[0].first == 2) {
199 p_v->setX(p_v->x() + 1.0);
200 } else if (dists[0].first == 3) {
201 p_v->setY(p_v->y() + 1.0);
202 } else {
203 p_v->setX(p_v->x() - 1.0);
204 }
205 }
206 qreal v1Length = std::sqrt(v1.x() * v1.x() + v1.y() * v1.y());
207 qreal v2Length = std::sqrt(v2.x() * v2.x() + v2.y() * v2.y());
208 // The macrocell will have a minimum size derived from the alignment but if
209 // its size doesn't contain enough pixels to cover all the range of
210 // intensities we expand it.
211 QSize macrocellTiles(1, 1);
212 while (macrocellTiles.width() * v1Length * macrocellTiles.height() * v2Length < 256) {
213 if (macrocellTiles.width() * v1Length > macrocellTiles.height() * v2Length) {
214 macrocellTiles.setHeight(macrocellTiles.height() + 1);
215 } else {
216 macrocellTiles.setWidth(macrocellTiles.width() + 1);
217 }
218 }
219 // Get size in cells of the macrocell
220 m_macrocellSize = QSize(alignX * macrocellTiles.width(), alignY * macrocellTiles.height());
221 const QSizeF macrocellSizeF = m_macrocellSize;
222 // Scale the top and left vectors and lengths to the final macrocell size
223 v1 *= macrocellTiles.width();
224 v2 *= macrocellTiles.height();
225 v1Length *= macrocellTiles.width();
226 v2Length *= macrocellTiles.height();
227 // Compute other useful quantities
228 const QPointF v3 = v1 + v2;
229 const QPointF l1 = v1;
230 const QPointF l2 = v2;
231 const QPointF l3 = -v1;
232 const QPointF l4 = -v2;
233 const qreal v1MicrocellLength = v1Length / macrocellSizeF.width();
234 const qreal v2MicrocellLength = v2Length / macrocellSizeF.height();
235 m_v1 = v1;
236 m_v2 = v2;
237 // Construct template<->screen transforms
238 const QPolygonF quad(
239 {
240 QPointF(0.0, 0.0),
241 v1 / macrocellSizeF.width(),
242 v1 / macrocellSizeF.width() + v2 / macrocellSizeF.height(),
243 v2 / macrocellSizeF.height()
244 }
245 );
246 QTransform::quadToSquare(quad, m_templateToScreenTransform);
248
249 // Construct the template
250
251 // Compute template dimensions
252 const QPoint topLeft(
253 static_cast<int>(qMin(0.0, qMin(v1.x(), qMin(v2.x(), v1.x() + v2.x())))),
254 static_cast<int>(qMin(0.0, qMin(v1.y(), qMin(v2.y(), v1.y() + v2.y()))))
255 );
256 const QPoint bottomRight(
257 static_cast<int>(qMax(0.0, qMax(v1.x(), qMax(v2.x(), v1.x() + v2.x())))),
258 static_cast<int>(qMax(0.0, qMax(v1.y(), qMax(v2.y(), v1.y() + v2.y()))))
259 );
260 // Add an 1 pixel border around the template, useful for bilinear interpolation
261 m_templateSize = QSize(bottomRight.x() - topLeft.x() + 2, bottomRight.y() - topLeft.y() + 2);
262 m_originOffset = -topLeft + QPoint(1, 1);
263
265
266 // Convenience struct to store some info for a single pixel during the
267 // construction of the template
268 struct AuxiliaryPoint
269 {
270 // Pixel index with respect to the template image
271 int templatePixelIndex;
272 // An pseudo index that orders the pixel with respect to a cell from
273 // top-left to bottom right
274 qreal microcellPixelIndex;
275 // Spot function value at the top-left corner of the pixel
276 qreal valueAtCorner;
277 // Spot function value at the center of the pixel
278 qreal valueAtCenter;
279 // Distance from the "center" of the shape to the top-left corner of the
280 // pixel
281 qreal distanceToCorner;
282 // Distance from the "center" of the shape to the center of the pixel
283 qreal distanceToCenter;
284 };
285 // Convenience struct to store some info for a single microcell during the
286 // construction of the template
287 struct AuxiliaryMicrocell
288 {
289 // The order in which this cell should light up pixels with respect to
290 // the other cells
291 int index;
292 // List of points in this cell
293 QVector<AuxiliaryPoint> auxiliaryPoints;
294 };
295
296 QVector<AuxiliaryMicrocell> auxiliaryMicrocells(m_macrocellSize.width() * m_macrocellSize.height());
297
298 // Set the ordered index for each auxiliary microcell
299 // Use makeCellOrderList to shuffle the microcell activation order so that
300 // they follow bayer matrix like patters. This allows to grow the
301 // microcells in a way that they don't visually cluster
302 QVector<int> microcellIndices = makeCellOrderList(m_macrocellSize.width(), m_macrocellSize.height());
303 for (int i = 0; i < auxiliaryMicrocells.size(); ++i) {
304 auxiliaryMicrocells[i].index = microcellIndices[i];
305 }
306
307 // "Rasterize" the macrocell: get all the points of the template that lie
308 // inside the macrocell and store them as auxiliary points that contain
309 // some info useful for sorting later.
310 // The template will have a representation of the complete transformed
311 // macrocell, but also some other areas outside (parts of adjacent
312 // macrocells) if, for example, the screen is rotated. To get later a value
313 // from the template we only use the pixels inside the macrocell, but the
314 // pixels outside are useful to perform bilinear interpolation
315
316 // Get the horizontal and vertical points at cell corners
317 QVector<QPointF> horizontalCellPositions;
318 for (int i = -1; i <= m_macrocellSize.width() + 1; ++i) {
319 horizontalCellPositions.append(v1 / static_cast<qreal>(m_macrocellSize.width()) * static_cast<qreal>(i));
320 }
321 QVector<QPointF> verticalCellPositions;
322 for (int i = -1; i <= m_macrocellSize.height() + 1; ++i) {
323 verticalCellPositions.append(v2 / static_cast<qreal>(m_macrocellSize.height()) * static_cast<qreal>(i));
324 }
325
326 int totalNumberOfAuxiliaryPoints = 0;
327
328 // Convenience function that tells if a pixel should belong to the left or
329 // to the right side of an edge. Used only when the point lies exactly in
330 // the edge
331 auto pointBelongsToLeftSideOfEdge = [](const QPointF &point, const QPointF &edgeStart, const QPointF &edgeDirection) -> bool
332 {
333 const qreal r = (point.x() - edgeStart.x()) * edgeDirection.y() - (point.y() - edgeStart.y()) * edgeDirection.x();
334 return (r == 0.0) && ((edgeDirection.y() == 0.0 && edgeDirection.x() > 0.0) || (edgeDirection.y() > 0.0));
335 };
336
337 // This is used to get an estimate of how far a given point is from the
338 // "center" of the shape (center of dot, center of line, etc.)
339 LightUpOrderingFunction<ScreentoneFunction> lightUpOrderingFunction;
340
341 // Visit all the pixels on the template and add an auxiliary point for each
342 // one that falls inside the macrocell
343 for (int i = 0; i < m_templateData.size(); ++i) {
344 // Transform the pixel position from template to screen coordinates.
345 // We transform both the point at the pixel corner (used for sampling
346 // the screentone function) and the point at pixel center (used to
347 // compute to which cell the pixel belongs)
348 const int templateY = i / m_templateSize.width();
349 const int templateX = i - m_templateSize.width() * templateY;
350 const QPointF pixelCornerPoint(
351 static_cast<qreal>(templateX - m_originOffset.x()),
352 static_cast<qreal>(templateY - m_originOffset.y())
353 );
354 const QPointF pixelCenterPoint(pixelCornerPoint + QPointF(0.5, 0.5));
355 const QPointF pixelCenterScreenPoint = m_templateToScreenTransform.map(pixelCenterPoint);
356 const QPointF macrocellPos(pixelCenterScreenPoint.x() * v1MicrocellLength, pixelCenterScreenPoint.y() * v2MicrocellLength);
357 // Initial estimate for the microcell position
358 int microcellX = static_cast<int>(std::floor(macrocellPos.x() * static_cast<qreal>(m_macrocellSize.width()) / v1Length));
359 int microcellY = static_cast<int>(std::floor(macrocellPos.y() * static_cast<qreal>(m_macrocellSize.height()) / v2Length));
360 // Discard this pixel if it is clearly outside the macrocell
361 if (microcellX < -1 || microcellX > m_macrocellSize.width() ||
362 microcellY < -1 || microcellY > m_macrocellSize.height()) {
363 continue;
364 }
365 // If the pixel center lies exactly in a border between cells we must
366 // decide if it should belong to a neighbor cell. This is an usual
367 // problem in rasterization. Here we treat the pixel as if it belonged
368 const QPointF topLeftCorner(horizontalCellPositions[microcellX + 1] + verticalCellPositions[microcellY + 1]);
369 const QPointF topRightCorner(horizontalCellPositions[microcellX + 1 + 1] + verticalCellPositions[microcellY + 1]);
370 const QPointF bottomRightCorner(horizontalCellPositions[microcellX + 1 + 1] + verticalCellPositions[microcellY + 1 + 1]);
371 const QPointF bottomLeftCorner(horizontalCellPositions[microcellX + 1] + verticalCellPositions[microcellY + 1 + 1]);
372 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, topLeftCorner, l1)) {
373 --microcellY;
374 }
375 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, topRightCorner, l2)) {
376 ++microcellX;
377 }
378 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, bottomRightCorner, l3)) {
379 ++microcellY;
380 }
381 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, bottomLeftCorner, l4)) {
382 --microcellX;
383 }
384 // Discard the pixel since the modified microcell position is for a cell
385 // that belongs to another macrocell
386 if (microcellX < 0 || microcellX >= m_macrocellSize.width() ||
387 microcellY < 0 || microcellY >= m_macrocellSize.height()) {
388 continue;
389 }
390 // Add the pixel to the auxiliary points collection
391 const int microcellIndex = microcellY * m_macrocellSize.width() + microcellX;
392 const qreal microcellPixelIndexX = macrocellPos.x() - static_cast<qreal>(microcellX) * v1MicrocellLength;
393 const qreal microcellPixelIndexY = macrocellPos.y() - static_cast<qreal>(microcellY) * v2MicrocellLength;
394 const qreal microcellPixelIndex = microcellPixelIndexY * v1MicrocellLength + microcellPixelIndexX;
395 const QPointF pixelCornerScreenPoint = m_templateToScreenTransform.map(pixelCornerPoint);
396 auxiliaryMicrocells[microcellIndex].auxiliaryPoints.push_back(
397 {
398 i,
399 microcellPixelIndex,
400 screentoneFunction(pixelCornerScreenPoint.x(), pixelCornerScreenPoint.y()),
401 screentoneFunction(pixelCenterScreenPoint.x(), pixelCenterScreenPoint.y()),
402 lightUpOrderingFunction(pixelCornerScreenPoint.x(), pixelCornerScreenPoint.y()),
403 lightUpOrderingFunction(pixelCenterScreenPoint.x(), pixelCenterScreenPoint.y()),
404 }
405 );
406 ++totalNumberOfAuxiliaryPoints;
407 }
408
409 // Sort the auxiliary points of each auxiliary microcell with respect to
410 // different criteria
411 for (int i = 0; i < auxiliaryMicrocells.size(); ++i) {
412 std::sort(auxiliaryMicrocells[i].auxiliaryPoints.begin(), auxiliaryMicrocells[i].auxiliaryPoints.end(),
413 [](const AuxiliaryPoint &a, const AuxiliaryPoint &b) {
414 if (qFuzzyCompare(a.valueAtCorner, b.valueAtCorner)) {
415 if (qFuzzyCompare(a.valueAtCenter, b.valueAtCenter)) {
416 if (qFuzzyCompare(a.distanceToCenter, b.distanceToCenter)) {
417 if (qFuzzyCompare(a.distanceToCorner, b.distanceToCorner)) {
418 return a.microcellPixelIndex < b.microcellPixelIndex;
419 }
420 return a.distanceToCorner < b.distanceToCorner;
421 }
422 return a.distanceToCenter < b.distanceToCenter;
423 }
424 return a.valueAtCenter < b.valueAtCenter;
425 }
426 return a.valueAtCorner < b.valueAtCorner;
427 }
428 );
429 }
430
431 // Sort the auxiliary microcells themselves
432 std::sort(auxiliaryMicrocells.begin(), auxiliaryMicrocells.end(),
433 [](const AuxiliaryMicrocell &a, const AuxiliaryMicrocell &b) {
434 return a.index < b.index;
435 }
436 );
437
438 // Fill the template pixels inside the macrocell with the sorted auxiliary
439 // points
440 {
441 int macrocellPixelIndex = 0;
442 QVector<int> microcellPixelIndices(auxiliaryMicrocells.size());
443 while (macrocellPixelIndex < totalNumberOfAuxiliaryPoints) {
444 for (int i = 0; i < auxiliaryMicrocells.size(); ++i) {
445 if (microcellPixelIndices[i] == auxiliaryMicrocells[i].auxiliaryPoints.size()) {
446 continue;
447 }
448 m_templateData[auxiliaryMicrocells[i].auxiliaryPoints[microcellPixelIndices[i]].templatePixelIndex] =
449 totalNumberOfAuxiliaryPoints - 1 == 0 ? 0 // to avoid UB from division by zero
450 : static_cast<qreal>(macrocellPixelIndex) / static_cast<qreal>(totalNumberOfAuxiliaryPoints - 1);
451 ++microcellPixelIndices[i];
452 ++macrocellPixelIndex;
453 }
454 }
455 }
456
457 // Fill the rest of the template pixels by copying the values from the
458 // already set ones
459 for (int i = 0; i < m_templateData.size(); ++i) {
460 if (m_templateData[i] < 0.0) {
461 int templateY = i / m_templateSize.width();
462 int templateX = i - m_templateSize.width() * templateY;
463 QPointF pixelCenterPoint(
464 static_cast<qreal>(templateX - m_originOffset.x()) + 0.5,
465 static_cast<qreal>(templateY - m_originOffset.y()) + 0.5
466 );
467 const QPointF pixelCenterScreenPoint = m_templateToScreenTransform.map(pixelCenterPoint);
468 const qreal a = -std::floor(pixelCenterScreenPoint.x() / macrocellSizeF.width());
469 const qreal b = -std::floor(pixelCenterScreenPoint.y() / macrocellSizeF.height());
470 pixelCenterPoint += QPointF(a * v1.x() + b * v2.x(), a * v1.y() + b * v2.y());
471
472 int x = static_cast<int>(std::floor(pixelCenterPoint.x())) + m_originOffset.x();
473 int y = static_cast<int>(std::floor(pixelCenterPoint.y())) + m_originOffset.y();
474 int macrocellPointIndex = y * m_templateSize.width() + x;
475
476 // if for some reason the target reference pixel is not set, we try
477 // find another one just by wrapping around the macrocell
478 if (m_templateData[macrocellPointIndex] < 0.0) {
479 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, QPointF(0.0, 0.0), l1)) {
480 pixelCenterPoint += v2;
481 }
482 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v1, l2)) {
483 pixelCenterPoint -= v1;
484 }
485 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v3, l3)) {
486 pixelCenterPoint -= v2;
487 }
488 if (pointBelongsToLeftSideOfEdge(pixelCenterPoint, v2, l4)) {
489 pixelCenterPoint += v1;
490 }
491 templateX = static_cast<int>(std::floor(pixelCenterPoint.x())) + m_originOffset.x();
492 templateY = static_cast<int>(std::floor(pixelCenterPoint.y())) + m_originOffset.y();
493 macrocellPointIndex = templateY * m_templateSize.width() + templateX;
494 }
495
496 m_templateData[i] = m_templateData[macrocellPointIndex];
497 }
498 }
499}
QVector< int > makeCellOrderList(int macrocellColumns, int macrocellRows) const
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

References KisScreentoneSizeMode_PixelBased, kisSquareDistance(), m_imageToScreenTransform, m_macrocellSize, m_originOffset, m_screenPosition, m_screenToTemplateTransform, m_templateData, m_templateSize, m_templateToScreenTransform, m_v1, m_v2, makeCellOrderList(), qFuzzyCompare(), qFuzzyIsNull(), v1(), and v2().

◆ originOffset()

const QPoint & KisScreentoneGeneratorTemplate::originOffset ( ) const
inline

Definition at line 30 of file KisScreentoneGeneratorTemplate.h.

30{ return m_originOffset; }

References m_originOffset.

◆ screenPosition()

const QPointF & KisScreentoneGeneratorTemplate::screenPosition ( ) const
inline

Definition at line 27 of file KisScreentoneGeneratorTemplate.h.

27{ return m_screenPosition; }

References m_screenPosition.

◆ screenToTemplateTransform()

const QTransform & KisScreentoneGeneratorTemplate::screenToTemplateTransform ( ) const
inline

Definition at line 25 of file KisScreentoneGeneratorTemplate.h.

References m_screenToTemplateTransform.

◆ templateData()

const QVector< qreal > & KisScreentoneGeneratorTemplate::templateData ( ) const
inline

Definition at line 23 of file KisScreentoneGeneratorTemplate.h.

23{ return m_templateData; }

References m_templateData.

◆ templateSize()

const QSize & KisScreentoneGeneratorTemplate::templateSize ( ) const
inline

Definition at line 29 of file KisScreentoneGeneratorTemplate.h.

29{ return m_templateSize; }

References m_templateSize.

◆ templateToScreenTransform()

const QTransform & KisScreentoneGeneratorTemplate::templateToScreenTransform ( ) const
inline

Definition at line 26 of file KisScreentoneGeneratorTemplate.h.

References m_templateToScreenTransform.

◆ v1()

const QPointF & KisScreentoneGeneratorTemplate::v1 ( ) const
inline

Definition at line 31 of file KisScreentoneGeneratorTemplate.h.

31{ return m_v1; }

References m_v1.

◆ v2()

const QPointF & KisScreentoneGeneratorTemplate::v2 ( ) const
inline

Definition at line 32 of file KisScreentoneGeneratorTemplate.h.

32{ return m_v2; }

References m_v2.

Member Data Documentation

◆ m_imageToScreenTransform

QTransform KisScreentoneGeneratorTemplate::m_imageToScreenTransform
private

Definition at line 36 of file KisScreentoneGeneratorTemplate.h.

◆ m_macrocellSize

QSize KisScreentoneGeneratorTemplate::m_macrocellSize
private

Definition at line 38 of file KisScreentoneGeneratorTemplate.h.

◆ m_originOffset

QPoint KisScreentoneGeneratorTemplate::m_originOffset
private

Definition at line 40 of file KisScreentoneGeneratorTemplate.h.

◆ m_screenPosition

QPointF KisScreentoneGeneratorTemplate::m_screenPosition
private

Definition at line 37 of file KisScreentoneGeneratorTemplate.h.

◆ m_screenToTemplateTransform

QTransform KisScreentoneGeneratorTemplate::m_screenToTemplateTransform
private

Definition at line 36 of file KisScreentoneGeneratorTemplate.h.

◆ m_templateData

QVector<qreal> KisScreentoneGeneratorTemplate::m_templateData
private

Definition at line 35 of file KisScreentoneGeneratorTemplate.h.

◆ m_templateSize

QSize KisScreentoneGeneratorTemplate::m_templateSize
private

Definition at line 39 of file KisScreentoneGeneratorTemplate.h.

◆ m_templateToScreenTransform

QTransform KisScreentoneGeneratorTemplate::m_templateToScreenTransform
private

Definition at line 36 of file KisScreentoneGeneratorTemplate.h.

◆ m_v1

QPointF KisScreentoneGeneratorTemplate::m_v1
private

Definition at line 41 of file KisScreentoneGeneratorTemplate.h.

◆ m_v2

QPointF KisScreentoneGeneratorTemplate::m_v2
private

Definition at line 41 of file KisScreentoneGeneratorTemplate.h.


The documentation for this class was generated from the following files: