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

#include <kis_qimage_pyramid.h>

Classes

struct  PyramidLevel
 

Public Member Functions

QImage createImage (KisDabShape const &, qreal subPixelX, qreal subPixelY) const
 
QImage getClosest (QTransform transform, qreal *scale) const
 
QImage getClosestWithoutWorkaroundBorder (QTransform transform, qreal *scale) const
 
 KisQImagePyramid ()=default
 
 KisQImagePyramid (const QImage &baseImage, bool useSmoothingForEnlarging=true)
 
 ~KisQImagePyramid ()
 

Static Public Member Functions

static QSizeF characteristicSize (const QSize &originalSize, KisDabShape const &)
 
static QSize imageSize (const QSize &originalSize, KisDabShape const &, qreal subPixelX, qreal subPixelY)
 

Private Member Functions

void appendPyramidLevel (const QImage &image)
 
int findNearestLevel (qreal scale, qreal *baseScale) const
 

Static Private Member Functions

static void calculateParams (KisDabShape const &shape, qreal subPixelX, qreal subPixelY, const QSize &originalSize, QTransform *outputTransform, QSize *outputSize)
 
static void calculateParams (KisDabShape shape, qreal subPixelX, qreal subPixelY, const QSize &originalSize, qreal baseScale, const QSize &baseSize, QTransform *outputTransform, QSize *outputSize)
 

Private Attributes

qreal m_baseScale {0.0}
 
QVector< PyramidLevelm_levels
 
QSize m_originalSize
 

Friends

class KisGbrBrushTest
 

Detailed Description

Definition at line 16 of file kis_qimage_pyramid.h.

Constructor & Destructor Documentation

◆ KisQImagePyramid() [1/2]

KisQImagePyramid::KisQImagePyramid ( )
default

◆ KisQImagePyramid() [2/2]

KisQImagePyramid::KisQImagePyramid ( const QImage & baseImage,
bool useSmoothingForEnlarging = true )

Definition at line 19 of file kis_qimage_pyramid.cpp.

20{
21 KIS_SAFE_ASSERT_RECOVER_RETURN(!baseImage.isNull());
22
23 m_originalSize = baseImage.size();
24
25
26 qreal scale = MAX_MIPMAP_SCALE;
27
28 while (scale > 1.0) {
29 QSize scaledSize = m_originalSize * scale;
30
31 if (scaledSize.width() <= MIPMAP_SIZE_THRESHOLD ||
32 scaledSize.height() <= MIPMAP_SIZE_THRESHOLD) {
33
34 if (m_levels.isEmpty()) {
36 }
37
38 if (useSmoothingForEnlarging) {
39 appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
40 } else {
41 appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation));
42 }
43 }
44
45 scale *= 0.5;
46 }
47
48 if (m_levels.isEmpty()) {
49 m_baseScale = 1.0;
50 }
51 appendPyramidLevel(baseImage);
52
53 scale = 0.5;
54 while (true) {
55 QSize scaledSize = m_originalSize * scale;
56
57 if (scaledSize.width() == 0 ||
58 scaledSize.height() == 0) break;
59
60 appendPyramidLevel(baseImage.scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
61
62 scale *= 0.5;
63 }
64}
void appendPyramidLevel(const QImage &image)
QVector< PyramidLevel > m_levels
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define MIPMAP_SIZE_THRESHOLD
#define MAX_MIPMAP_SCALE

References appendPyramidLevel(), KIS_SAFE_ASSERT_RECOVER_RETURN, m_baseScale, m_levels, m_originalSize, MAX_MIPMAP_SCALE, and MIPMAP_SIZE_THRESHOLD.

◆ ~KisQImagePyramid()

KisQImagePyramid::~KisQImagePyramid ( )

Definition at line 66 of file kis_qimage_pyramid.cpp.

67{
68}

Member Function Documentation

◆ appendPyramidLevel()

void KisQImagePyramid::appendPyramidLevel ( const QImage & image)
private

QPainter has a bug: when doing a transformation it decides that all the pixels outside of the image (source rect) are equal to the border pixels (CLAMP in terms of openGL). This means that there will be no smooth scaling on the border of the image when it is rotated. To workaround this bug we need to add one pixel wide border to the image, so that it transforms smoothly.

See a unittest in: KisGbrBrushTest::testQPainterTransformationBorder

Definition at line 238 of file kis_qimage_pyramid.cpp.

239{
251QSize levelSize = image.size();
252 QImage tmp = image.convertToFormat(QImage::Format_ARGB32);
255 image.width() + 2 * QPAINTER_WORKAROUND_BORDER,
256 image.height() + 2 * QPAINTER_WORKAROUND_BORDER);
257 m_levels.append(PyramidLevel(tmp, levelSize));
258}
#define QPAINTER_WORKAROUND_BORDER

References m_levels, and QPAINTER_WORKAROUND_BORDER.

◆ calculateParams() [1/2]

void KisQImagePyramid::calculateParams ( KisDabShape const & shape,
qreal subPixelX,
qreal subPixelY,
const QSize & originalSize,
QTransform * outputTransform,
QSize * outputSize )
staticprivate

Definition at line 134 of file kis_qimage_pyramid.cpp.

138{
139 calculateParams(shape,
140 subPixelX, subPixelY,
141 originalSize, 1.0, originalSize,
142 outputTransform, outputSize);
143}
static void calculateParams(KisDabShape const &shape, qreal subPixelX, qreal subPixelY, const QSize &originalSize, QTransform *outputTransform, QSize *outputSize)

References calculateParams().

◆ calculateParams() [2/2]

void KisQImagePyramid::calculateParams ( KisDabShape shape,
qreal subPixelX,
qreal subPixelY,
const QSize & originalSize,
qreal baseScale,
const QSize & baseSize,
QTransform * outputTransform,
QSize * outputSize )
staticprivate

Definition at line 145 of file kis_qimage_pyramid.cpp.

150{
151 Q_UNUSED(baseScale);
152
153 QRectF originalBounds = QRectF(QPointF(), originalSize);
154 QTransform originalTransform = baseBrushTransform(shape, subPixelX, subPixelY, originalBounds);
155
156 qreal realBaseScaleX = qreal(baseSize.width()) / originalSize.width();
157 qreal realBaseScaleY = qreal(baseSize.height()) / originalSize.height();
158 qreal scaleX = shape.scaleX() / realBaseScaleX;
159 qreal scaleY = shape.scaleY() / realBaseScaleY;
160 shape = KisDabShape(scaleX, scaleY/scaleX, shape.rotation());
161
162 QRectF baseBounds = QRectF(QPointF(), baseSize);
163 QTransform transform = baseBrushTransform(shape, subPixelX, subPixelY, baseBounds);
164 QRectF mappedRect = originalTransform.mapRect(originalBounds);
165
166 // Set up a 0,0,1,1 size and identity transform in case the transform fails to
167 // produce a usable result.
168 int width = 1;
169 int height = 1;
170 *outputTransform = QTransform();
171
172 if (mappedRect.isValid()) {
173 QRect expectedDstRect = roundRect(mappedRect);
174
175#if 0 // Only enable when debugging; users shouldn't see this warning
176 {
177 QRect testingRect = roundRect(transform.mapRect(baseBounds));
178 if (testingRect != expectedDstRect) {
179 warnKrita << "WARNING: expected and real dab rects do not coincide!";
180 warnKrita << " expected rect:" << expectedDstRect;
181 warnKrita << " real rect: " << testingRect;
182 }
183 }
184#endif
185 KIS_SAFE_ASSERT_RECOVER_NOOP(expectedDstRect.x() >= 0);
186 KIS_SAFE_ASSERT_RECOVER_NOOP(expectedDstRect.y() >= 0);
187
188 width = expectedDstRect.x() + expectedDstRect.width();
189 height = expectedDstRect.y() + expectedDstRect.height();
190
191 // we should not return invalid image, so adjust the image to be
192 // at least 1 px in size.
193 width = qMax(1, width);
194 height = qMax(1, height);
195 }
196 else {
197#if 0 // Only enable when debugging; users shouldn't see this warning
198 qWarning() << "Brush transform generated an invalid rectangle!"
199 << ppVar(shape.scaleX()) << ppVar(shape.scaleY()) << ppVar(shape.rotation())
200 << ppVar(subPixelX) << ppVar(subPixelY)
201 << ppVar(originalSize)
202 << ppVar(baseScale)
203 << ppVar(baseSize)
204 << ppVar(baseBounds)
205 << ppVar(mappedRect);
206#endif
207 }
208
209 *outputTransform = transform;
210 *outputSize = QSize(width, height);
211}
qreal scaleY() const
qreal scaleX() const
qreal rotation() const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define warnKrita
Definition kis_debug.h:87
#define ppVar(var)
Definition kis_debug.h:155
QRect roundRect(const QRectF &rc)
QTransform baseBrushTransform(KisDabShape const &shape, qreal subPixelX, qreal subPixelY, const QRectF &baseBounds)

References baseBrushTransform(), KIS_SAFE_ASSERT_RECOVER_NOOP, ppVar, KisDabShape::rotation(), roundRect(), KisDabShape::scaleX(), KisDabShape::scaleY(), and warnKrita.

◆ characteristicSize()

QSizeF KisQImagePyramid::characteristicSize ( const QSize & originalSize,
KisDabShape const & shape )
static

Definition at line 227 of file kis_qimage_pyramid.cpp.

229{
230 QRectF originalRect(QPointF(), originalSize);
231 QTransform transform = baseBrushTransform(shape,
232 0.0, 0.0,
233 originalRect);
234
235 return transform.mapRect(originalRect).size();
236}

References baseBrushTransform().

◆ createImage()

QImage KisQImagePyramid::createImage ( KisDabShape const & shape,
qreal subPixelX,
qreal subPixelY ) const

QPainter has one more bug: when a QTransform is TxTranslate, it does wrong sampling (probably, Nearest Neighbour) even though we tell it directly that we need SmoothPixmapTransform.

So here is a workaround: we set a negligible scale to convince Qt we use a non-only-translating transform.

Definition at line 260 of file kis_qimage_pyramid.cpp.

262{
263 if (m_levels.isEmpty()) return QImage();
264
265 qreal baseScale = -1.0;
266 int level = findNearestLevel(shape.scale(), &baseScale);
267
268 const QImage &srcImage = m_levels[level].image;
269
270 QTransform transform;
271 QSize dstSize;
272
273 calculateParams(shape, subPixelX, subPixelY,
274 m_originalSize, baseScale, m_levels[level].size,
275 &transform, &dstSize);
276
277 if (transform.isIdentity() &&
278 srcImage.format() == QImage::Format_ARGB32) {
279
280 return srcImage.copy(QPAINTER_WORKAROUND_BORDER,
282 srcImage.width() - 2 * QPAINTER_WORKAROUND_BORDER,
283 srcImage.height() - 2 * QPAINTER_WORKAROUND_BORDER);
284 }
285
286 QImage dstImage(dstSize, QImage::Format_ARGB32);
287 dstImage.fill(0);
288
289
298 while (transform.type() == QTransform::TxTranslate) {
299 const qreal scale = transform.m11();
300 const qreal fakeScale = scale - 10 * std::numeric_limits<qreal>::epsilon();
301 transform *= QTransform::fromScale(fakeScale, fakeScale);
302 }
303
304 QPainter gc(&dstImage);
305 gc.setTransform(
306 QTransform::fromTranslate(-QPAINTER_WORKAROUND_BORDER,
307 -QPAINTER_WORKAROUND_BORDER) * transform);
308 gc.setRenderHints(QPainter::SmoothPixmapTransform);
309 gc.drawImage(QPointF(), srcImage);
310 gc.end();
311
312 return dstImage;
313}
int findNearestLevel(qreal scale, qreal *baseScale) const

References calculateParams(), findNearestLevel(), m_levels, m_originalSize, QPAINTER_WORKAROUND_BORDER, and KisDabShape::scale().

◆ findNearestLevel()

int KisQImagePyramid::findNearestLevel ( qreal scale,
qreal * baseScale ) const
private

Definition at line 70 of file kis_qimage_pyramid.cpp.

71{
72 const qreal scale_epsilon = 1e-6;
73
74 qreal levelScale = m_baseScale;
75 int level = 0;
76 int lastLevel = m_levels.size() - 1;
77
78
79 while ((0.5 * levelScale > scale ||
80 qAbs(0.5 * levelScale - scale) < scale_epsilon) &&
81 level < lastLevel) {
82
83 levelScale *= 0.5;
84 level++;
85 }
86
87 *baseScale = levelScale;
88 return level;
89}

References m_baseScale, and m_levels.

◆ getClosest()

QImage KisQImagePyramid::getClosest ( QTransform transform,
qreal * scale ) const

Definition at line 315 of file kis_qimage_pyramid.cpp.

316{
317 if (m_levels.isEmpty()) return QImage();
318
319 // Estimate scale
320 QSizeF transformedUnitSquare = transform.mapRect(QRectF(0, 0, 1, 1)).size();
321 qreal x = qAbs(transformedUnitSquare.width());
322 qreal y = qAbs(transformedUnitSquare.height());
323 qreal estimatedScale = (x > y) ? transformedUnitSquare.width() : transformedUnitSquare.height();
324
325 int level = findNearestLevel(estimatedScale, scale);
326 return m_levels[level].image;
327}

References findNearestLevel(), and m_levels.

◆ getClosestWithoutWorkaroundBorder()

QImage KisQImagePyramid::getClosestWithoutWorkaroundBorder ( QTransform transform,
qreal * scale ) const

Definition at line 329 of file kis_qimage_pyramid.cpp.

330{
331 QImage image = getClosest(transform, scale);
332 return image.copy(QPAINTER_WORKAROUND_BORDER,
334 image.width() - 2 * QPAINTER_WORKAROUND_BORDER,
335 image.height() - 2 * QPAINTER_WORKAROUND_BORDER);
336}
QImage getClosest(QTransform transform, qreal *scale) const

References getClosest(), and QPAINTER_WORKAROUND_BORDER.

◆ imageSize()

QSize KisQImagePyramid::imageSize ( const QSize & originalSize,
KisDabShape const & shape,
qreal subPixelX,
qreal subPixelY )
static

Definition at line 213 of file kis_qimage_pyramid.cpp.

216{
217 QTransform transform;
218 QSize dstSize;
219
220 calculateParams(shape, subPixelX, subPixelY,
221 originalSize,
222 &transform, &dstSize);
223
224 return dstSize;
225}

References calculateParams().

Friends And Related Symbol Documentation

◆ KisGbrBrushTest

friend class KisGbrBrushTest
friend

Definition at line 37 of file kis_qimage_pyramid.h.

Member Data Documentation

◆ m_baseScale

qreal KisQImagePyramid::m_baseScale {0.0}
private

Definition at line 54 of file kis_qimage_pyramid.h.

54{0.0};

◆ m_levels

QVector<PyramidLevel> KisQImagePyramid::m_levels
private

Definition at line 64 of file kis_qimage_pyramid.h.

◆ m_originalSize

QSize KisQImagePyramid::m_originalSize
private

Definition at line 53 of file kis_qimage_pyramid.h.


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