2
2
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de> filters
3
3
* Copyright (c) 2005-2007 Casper Boemann <cbr@boemann.dk>
4
4
* Copyright (c) 2005, 2010 Boudewijn Rempt <boud@valdyas.org>
5
* Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
6
7
* This program is free software; you can redistribute it and/or modify
7
8
* it under the terms of the GNU General Public License as published by
38
40
KisTransformWorker::KisTransformWorker(KisPaintDeviceSP dev,
39
41
double xscale, double yscale,
40
42
double xshear, double yshear,
43
double xshearOrigin, double yshearOrigin,
42
45
qint32 xtranslate, qint32 ytranslate,
43
46
KoUpdaterPtr progress,
55
m_xshearOrigin = xshearOrigin;
56
m_yshearOrigin = yshearOrigin;
52
57
m_rotation = rotation,
53
m_xtranslate = xtranslate;
58
m_xtranslate = xtranslate;
54
59
m_ytranslate = ytranslate;
55
60
m_progressUpdater = progress;
64
69
void KisTransformWorker::rotateNone(KisPaintDeviceSP src, KisPaintDeviceSP dst)
66
71
qint32 pixelSize = src->pixelSize();
68
73
KoColorSpace *cs = src->colorSpace();
71
r = src->exactBounds();
73
76
KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), r.top(), r.width());
74
77
KisHLineIterator vit = dst->createHLineIterator(r.x(), r.top(), r.width());
75
78
for (qint32 i = 0; i < r.height(); ++i) {
97
100
void KisTransformWorker::rotateRight90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
99
102
qint32 pixelSize = src->pixelSize();
103
QRect r(m_boundRect);
101
104
KoColorSpace *cs = src->colorSpace();
103
r = src->exactBounds();
105
107
for (qint32 y = r.bottom(); y >= r.top(); --y) {
106
108
KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width());
129
m_boundRect = QRect(- r.bottom(), r.x(), r.height(), r.width());
128
132
void KisTransformWorker::rotateLeft90(KisPaintDeviceSP src, KisPaintDeviceSP dst)
130
134
qint32 pixelSize = src->pixelSize();
135
QRect r(m_boundRect);
132
136
KoColorSpace *cs = src->colorSpace();
134
r = src->exactBounds();
136
139
KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), r.top(), r.width());
165
m_boundRect = QRect(r.top(), - r.x() - r.width(), r.height(), r.width());
163
168
void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst)
165
170
qint32 pixelSize = src->pixelSize();
171
QRect r(m_boundRect);
167
172
KoColorSpace *cs = src->colorSpace();
169
r = src->exactBounds();
171
175
KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), r.top(), r.width());
199
m_boundRect = QRect(- r.x() - r.width(), - r.bottom(), r.width(), r.height());
196
202
template <class iter> iter createIterator(KisPaintDevice *dev, qint32 start, qint32 lineNum, qint32 len);
207
213
return dev->createVLineIterator(lineNum, start, len);
210
template <class iter> void calcDimensions(KisPaintDevice *dev, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines);
216
template <class iter> void calcDimensions(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines);
212
218
template <> void calcDimensions <KisHLineIteratorPixel>
213
(KisPaintDevice *dev, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
219
(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
215
QRect rc = dev->exactBounds();
216
221
srcStart = rc.x();
217
222
firstLine = rc.y();
218
223
srcLen = rc.width();
222
227
template <> void calcDimensions <KisVLineIteratorPixel>
223
(KisPaintDevice *dev, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
228
(QRect rc, qint32 &srcStart, qint32 &srcLen, qint32 &firstLine, qint32 &numLines)
225
QRect rc = dev->exactBounds();
226
230
firstLine = rc.x();
227
231
srcStart = rc.y();
228
232
numLines = rc.width();
229
233
srcLen = rc.height();
236
template <class iter> void updateBounds(QRect &boundRect, double floatscale, double shear, qint32 dx);
238
template <> void updateBounds <KisHLineIteratorPixel>
239
(QRect &boundRect, double floatscale, double shear, qint32 dx)
241
QPoint topLeft(qFloor(boundRect.left() * floatscale + boundRect.top() * shear + dx), boundRect.top());
242
QPoint topRight(qCeil(boundRect.right() * floatscale + boundRect.top() * shear + dx), boundRect.top());
243
QPoint bottomRight(qCeil(boundRect.right() * floatscale + boundRect.bottom() * shear + dx), boundRect.bottom());
244
QPoint bottomLeft(qFloor(boundRect.left() * floatscale + boundRect.bottom() * shear + dx), boundRect.bottom());
246
p << topLeft << topRight << bottomRight << bottomLeft;
247
boundRect = p.boundingRect();
250
template <> void updateBounds <KisVLineIteratorPixel>
251
(QRect &boundRect, double floatscale, double shear, qint32 dx)
253
QPoint topLeft(boundRect.left(), qFloor(boundRect.top() * floatscale + boundRect.left() * shear + dx));
254
QPoint topRight(boundRect.right(), qFloor(boundRect.top() * floatscale + boundRect.right() * shear + dx));
255
QPoint bottomRight(boundRect.right(), qCeil(boundRect.bottom() * floatscale + boundRect.right() * shear + dx));
256
QPoint bottomLeft(boundRect.left(), qCeil(boundRect.bottom() * floatscale + boundRect.left() * shear + dx));
258
p << topLeft << topRight << bottomRight << bottomLeft;
259
boundRect = p.boundingRect();
232
262
struct FilterValues {
233
263
quint8 numWeights;
254
284
qint32 shearFracOffset;
257
calcDimensions <T>(src, srcStart, srcLen, firstLine, numLines);
287
calcDimensions <T>(m_boundRect, srcStart, srcLen, firstLine, numLines);
259
289
scale = int(floatscale * srcLen + 0.5);
260
295
scaleDenom = srcLen;
297
if (scaleDenom == 0) {
298
updateBounds <T>(m_boundRect, floatscale, shear, dx);
265
302
qint32 support = filterStrategy->intSupport();
266
303
qint32 dstLen, dstStart;
360
397
while (i < srcLen + extraLen) {
361
398
data = srcIt.rawData();
362
399
memcpy(&tmpLine[i*pixelSize], data, pixelSize);
363
cs->setOpacity(data, quint8(0), 1);
400
memcpy(data, src->defaultPixel(), pixelSize);
364
401
if (i < srcLen + extraLen - 1) // duplicate pixels along edge
426
463
delete [] colors;
427
464
delete [] tmpLine;
428
465
delete [] filterWeights;
467
updateBounds <T>(m_boundRect, floatscale, shear, dx);
431
470
bool KisTransformWorker::run()
472
/* Check for nonsense and let the user know, this helps debugging.
473
Otherwise the program will crash at a later point, in a very obscure way, probably by division by zero */
474
Q_ASSERT_X(m_xscale != 0, "KisTransformer::run() validation step", "xscale == 0");
475
Q_ASSERT_X(m_yscale != 0, "KisTransformer::run() validation step", "yscale == 0");
476
// Fallback safety line in case Krita is compiled without ASSERTS
477
if (m_xscale == 0 || m_yscale == 0) return false;
435
480
m_progressTotalSteps = 0;
436
481
m_progressStep = 0;
437
QRect r = m_dev->exactBounds();
483
KoColor defaultPixel(m_dev->defaultPixel(), m_dev->colorSpace());
484
if (defaultPixel.opacityU8() != OPACITY_TRANSPARENT_U8)
485
m_boundRect = m_dev->dataManager()->extent();
487
m_boundRect = m_dev->exactBounds();
489
if (m_boundRect.isNull()) {
490
if (!m_progressUpdater.isNull())
491
m_progressUpdater->setProgress(100);
439
495
KisPaintDeviceSP tmpdev1 = KisPaintDeviceSP(new KisPaintDevice(m_dev->colorSpace()));
440
496
KisPaintDeviceSP srcdev = m_dev;
447
503
qint32 xtranslate = m_xtranslate;
448
504
qint32 ytranslate = m_ytranslate;
506
m_progressTotalSteps = 0;
508
// Apply shear X and Y
509
if (xshear != 0 || yshear != 0) {
510
m_progressTotalSteps += (yscale * m_boundRect.height() * (m_boundRect.width() + m_xshear * m_boundRect.height()));
511
m_progressTotalSteps += (xscale * m_boundRect.width() * (m_boundRect.height() + m_yshear * m_boundRect.width()));
513
int dx = - qRound(m_yshearOrigin * yscale * m_xshear);
514
int dy = - qRound(m_xshearOrigin * xscale * m_yshear);
515
transformPass <KisHLineIteratorPixel>(srcdev.data(), srcdev.data(), xscale, yscale * m_xshear, dx, m_filter, m_fixBorderAlpha);
516
transformPass <KisVLineIteratorPixel>(srcdev.data(), srcdev.data(), yscale, m_yshear, dy, m_filter, m_fixBorderAlpha);
450
521
if (rotation < 0.0)
451
522
rotation = -fmod(-rotation, 2 * M_PI) + 2 * M_PI;
458
529
switch (rotQuadrant) {
459
530
default: // just to shut up the compiler
461
m_progressTotalSteps = 0;
532
m_progressTotalSteps += 0;
464
535
rotation -= M_PI / 2;
468
m_progressTotalSteps = r.width() * r.height();
539
m_progressTotalSteps += m_boundRect.width() * m_boundRect.height();
471
542
rotation -= M_PI;
472
m_progressTotalSteps = r.width() * r.height();
543
m_progressTotalSteps += m_boundRect.width() * m_boundRect.height();
475
546
rotation -= -M_PI / 2 + 2 * M_PI;
479
m_progressTotalSteps = r.width() * r.height();
550
m_progressTotalSteps += m_boundRect.width() * m_boundRect.height();
483
// Calculate some auxillary values
554
//// Calculate some auxillary values
484
555
yshear = sin(rotation);
485
556
xshear = -tan(rotation / 2);
486
557
xtranslate -= int(xshear * ytranslate);
488
559
// Calculate progress steps
489
m_progressTotalSteps += int(yscale * r.width() * r.height());
490
m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width() * yshear));
492
m_lastProgressReport = 0;
560
m_progressTotalSteps += int(yscale * m_boundRect.width() * m_boundRect.height());
561
m_progressTotalSteps += int(xscale * m_boundRect.width() * (m_boundRect.height() * yscale + m_boundRect.width() * yshear));
494
563
// Now that we have everything in place it's time to do the actual right angle rotations
495
564
switch (rotQuadrant) {
516
// Handle simple move case possibly with rotation of 90,180,270
585
//// Handle simple move case possibly with rotation of 90,180,270
517
586
if (rotation == 0.0 && xscale == 1.0 && yscale == 1.0) {
587
m_boundRect.translate(xtranslate, ytranslate);
518
588
if (rotQuadrant == 0) {
519
589
// When we didn't move the m_dev to a temp device we can simply just move its coords
520
590
srcdev->move(srcdev->x() + xtranslate, srcdev->y() + ytranslate);
536
606
transformPass <KisHLineIteratorPixel>(srcdev.data(), srcdev.data(), xscale, yscale*xshear, 0, m_filter, m_fixBorderAlpha);
538
608
if (!m_progressUpdater.isNull() && m_progressUpdater->interrupted()) {
552
622
transformPass <KisHLineIteratorPixel>(srcdev.data(), m_dev.data(), 1.0, xshear, xtranslate, m_filter, m_fixBorderAlpha);
554
624
// No need to filter again when we are only scaling
555
625
srcdev->move(srcdev->x() + xtranslate, srcdev->y());
556
626
if (rotQuadrant != 0) // no need to copy back if we have not copied the device in the first place
576
646
r = selection->selectedExactRect();
578
r = dev->exactBounds();
648
KoColor defaultPixel(dev->defaultPixel(), dev->colorSpace());
649
if (defaultPixel.opacityU8() != OPACITY_TRANSPARENT_U8)
650
r = dev->dataManager()->extent();
652
r = dev->exactBounds();
581
655
KisHLineConstIteratorPixel srcIt = dev->createHLineConstIterator(r.x(), r.top(), r.width(), selection);
622
696
r = selection->selectedExactRect();
624
r = dev->exactBounds();
698
KoColor defaultPixel(dev->defaultPixel(), dev->colorSpace());
699
if (defaultPixel.opacityU8() != OPACITY_TRANSPARENT_U8)
700
r = dev->dataManager()->extent();
702
r = dev->exactBounds();