120{
121
122
123
124
125 qreal sizeX, sizeY;
126 const int alignX = config->alignToPixelGrid() ? config->alignToPixelGridX() : 1;
127 const int alignY = config->alignToPixelGrid() ? config->alignToPixelGridY() : 1;
129 const bool constrainSize = config->constrainSize();
130 sizeX = config->sizeX();
131
132
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
139
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();
147 const qreal shearX = config->shearX();
148 const qreal shearY = config->shearY();
149 const qreal rotation = config->rotation();
150
155 QTransform screenToImage;
156 screenToImage.rotate(-rotation);
157 screenToImage.scale(sizeX, sizeY);
158 screenToImage.shear(-shearX, -shearY);
159
160
161
162
163
164
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
170
171
174
175
178 const QPointF *p_u = dist1 > dist2 ? &u1 : &u2;
179 QPointF *p_v = dist1 > dist2 ? &
v1 : &
v2;
180
181
182 QPair<int, qreal> dists[4]{
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
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
209
210
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
220 m_macrocellSize = QSize(alignX * macrocellTiles.width(), alignY * macrocellTiles.height());
222
223 v1 *= macrocellTiles.width();
224 v2 *= macrocellTiles.height();
225 v1Length *= macrocellTiles.width();
226 v2Length *= macrocellTiles.height();
227
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();
237
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 );
248
249
250
251
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
261 m_templateSize = QSize(bottomRight.x() - topLeft.x() + 2, bottomRight.y() - topLeft.y() + 2);
263
265
266
267
268 struct AuxiliaryPoint
269 {
270
271 int templatePixelIndex;
272
273
274 qreal microcellPixelIndex;
275
276 qreal valueAtCorner;
277
278 qreal valueAtCenter;
279
280
281 qreal distanceToCorner;
282
283 qreal distanceToCenter;
284 };
285
286
287 struct AuxiliaryMicrocell
288 {
289
290
291 int index;
292
294 };
295
297
298
299
300
301
303 for (int i = 0; i < auxiliaryMicrocells.size(); ++i) {
304 auxiliaryMicrocells[i].index = microcellIndices[i];
305 }
306
307
308
309
310
311
312
313
314
315
316
319 horizontalCellPositions.append(
v1 /
static_cast<qreal
>(
m_macrocellSize.width()) *
static_cast<qreal
>(i));
320 }
323 verticalCellPositions.append(
v2 /
static_cast<qreal
>(
m_macrocellSize.height()) *
static_cast<qreal
>(i));
324 }
325
326 int totalNumberOfAuxiliaryPoints = 0;
327
328
329
330
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
338
340
341
342
344
345
346
347
350 const QPointF pixelCornerPoint(
353 );
354 const QPointF pixelCenterPoint(pixelCornerPoint + QPointF(0.5, 0.5));
356 const QPointF macrocellPos(pixelCenterScreenPoint.x() * v1MicrocellLength, pixelCenterScreenPoint.y() * v2MicrocellLength);
357
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
363 continue;
364 }
365
366
367
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
385
388 continue;
389 }
390
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;
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
410
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
432 std::sort(auxiliaryMicrocells.begin(), auxiliaryMicrocells.end(),
433 [](const AuxiliaryMicrocell &a, const AuxiliaryMicrocell &b) {
434 return a.index < b.index;
435 }
436 );
437
438
439
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
450 : static_cast<qreal>(macrocellPixelIndex) / static_cast<qreal>(totalNumberOfAuxiliaryPoints - 1);
451 ++microcellPixelIndices[i];
452 ++macrocellPixelIndex;
453 }
454 }
455 }
456
457
458
463 QPointF pixelCenterPoint(
466 );
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();
475
476
477
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
497 }
498 }
499}
@ KisScreentoneSizeMode_PixelBased
QTransform m_templateToScreenTransform
const QPointF & v2() const
QTransform m_screenToTemplateTransform
QVector< int > makeCellOrderList(int macrocellColumns, int macrocellRows) const
QVector< qreal > m_templateData
const QPointF & v1() const
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)