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 251 of file kis_qimage_pyramid.cpp.

252{
264QSize levelSize = image.size();
265 QImage tmp = image.convertToFormat(QImage::Format_ARGB32);
268 image.width() + 2 * QPAINTER_WORKAROUND_BORDER,
269 image.height() + 2 * QPAINTER_WORKAROUND_BORDER);
270 m_levels.append(PyramidLevel(tmp, levelSize));
271}
#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 147 of file kis_qimage_pyramid.cpp.

151{
152 calculateParams(shape,
153 subPixelX, subPixelY,
154 originalSize, 1.0, originalSize,
155 outputTransform, outputSize);
156}
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 158 of file kis_qimage_pyramid.cpp.

163{
164 Q_UNUSED(baseScale);
165
166 QRectF originalBounds = QRectF(QPointF(), originalSize);
167 QTransform originalTransform = baseBrushTransform(shape, subPixelX, subPixelY, originalBounds);
168
169 qreal realBaseScaleX = qreal(baseSize.width()) / originalSize.width();
170 qreal realBaseScaleY = qreal(baseSize.height()) / originalSize.height();
171 qreal scaleX = shape.scaleX() / realBaseScaleX;
172 qreal scaleY = shape.scaleY() / realBaseScaleY;
173 shape = KisDabShape(scaleX, scaleY/scaleX, shape.rotation());
174
175 QRectF baseBounds = QRectF(QPointF(), baseSize);
176 QTransform transform = baseBrushTransform(shape, subPixelX, subPixelY, baseBounds);
177 QRectF mappedRect = originalTransform.mapRect(originalBounds);
178
179 // Set up a 0,0,1,1 size and identity transform in case the transform fails to
180 // produce a usable result.
181 int width = 1;
182 int height = 1;
183 *outputTransform = QTransform();
184
185 if (mappedRect.isValid()) {
186 QRect expectedDstRect = roundRect(mappedRect);
187
188#if 0 // Only enable when debugging; users shouldn't see this warning
189 {
190 QRect testingRect = roundRect(transform.mapRect(baseBounds));
191 if (testingRect != expectedDstRect) {
192 warnKrita << "WARNING: expected and real dab rects do not coincide!";
193 warnKrita << " expected rect:" << expectedDstRect;
194 warnKrita << " real rect: " << testingRect;
195 }
196 }
197#endif
198 KIS_SAFE_ASSERT_RECOVER_NOOP(expectedDstRect.x() >= 0);
199 KIS_SAFE_ASSERT_RECOVER_NOOP(expectedDstRect.y() >= 0);
200
201 width = expectedDstRect.x() + expectedDstRect.width();
202 height = expectedDstRect.y() + expectedDstRect.height();
203
204 // we should not return invalid image, so adjust the image to be
205 // at least 1 px in size.
206 width = qMax(1, width);
207 height = qMax(1, height);
208 }
209 else {
210#if 0 // Only enable when debugging; users shouldn't see this warning
211 qWarning() << "Brush transform generated an invalid rectangle!"
212 << ppVar(shape.scaleX()) << ppVar(shape.scaleY()) << ppVar(shape.rotation())
213 << ppVar(subPixelX) << ppVar(subPixelY)
214 << ppVar(originalSize)
215 << ppVar(baseScale)
216 << ppVar(baseSize)
217 << ppVar(baseBounds)
218 << ppVar(mappedRect);
219#endif
220 }
221
222 *outputTransform = transform;
223 *outputSize = QSize(width, height);
224}
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 240 of file kis_qimage_pyramid.cpp.

242{
243 QRectF originalRect(QPointF(), originalSize);
244 QTransform transform = baseBrushTransform(shape,
245 0.0, 0.0,
246 originalRect);
247
248 return transform.mapRect(originalRect).size();
249}

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 273 of file kis_qimage_pyramid.cpp.

275{
276 if (m_levels.isEmpty()) return QImage();
277
278 qreal baseScale = -1.0;
279 int level = findNearestLevel(shape.scale(), &baseScale);
280
281 const QImage &srcImage = m_levels[level].image;
282
283 QTransform transform;
284 QSize dstSize;
285
286 calculateParams(shape, subPixelX, subPixelY,
287 m_originalSize, baseScale, m_levels[level].size,
288 &transform, &dstSize);
289
290 if (transform.isIdentity() &&
291 srcImage.format() == QImage::Format_ARGB32) {
292
293 return srcImage.copy(QPAINTER_WORKAROUND_BORDER,
295 srcImage.width() - 2 * QPAINTER_WORKAROUND_BORDER,
296 srcImage.height() - 2 * QPAINTER_WORKAROUND_BORDER);
297 }
298
299 QImage dstImage(dstSize, QImage::Format_ARGB32);
300 dstImage.fill(0);
301
302
311 while (transform.type() == QTransform::TxTranslate) {
312 const qreal scale = transform.m11();
313 const qreal fakeScale = scale - 10 * std::numeric_limits<qreal>::epsilon();
314 transform *= QTransform::fromScale(fakeScale, fakeScale);
315 }
316
317 QPainter gc(&dstImage);
318 gc.setTransform(
319 QTransform::fromTranslate(-QPAINTER_WORKAROUND_BORDER,
320 -QPAINTER_WORKAROUND_BORDER) * transform);
321 gc.setRenderHints(QPainter::SmoothPixmapTransform);
322 gc.drawImage(QPointF(), srcImage);
323 gc.end();
324
325 return dstImage;
326}
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 328 of file kis_qimage_pyramid.cpp.

329{
330 if (m_levels.isEmpty()) return QImage();
331
332 // Estimate scale
333 QSizeF transformedUnitSquare = transform.mapRect(QRectF(0, 0, 1, 1)).size();
334 qreal x = qAbs(transformedUnitSquare.width());
335 qreal y = qAbs(transformedUnitSquare.height());
336 qreal estimatedScale = (x > y) ? transformedUnitSquare.width() : transformedUnitSquare.height();
337
338 int level = findNearestLevel(estimatedScale, scale);
339 return m_levels[level].image;
340}

References findNearestLevel(), and m_levels.

◆ getClosestWithoutWorkaroundBorder()

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

Definition at line 342 of file kis_qimage_pyramid.cpp.

343{
344 QImage image = getClosest(transform, scale);
345 return image.copy(QPAINTER_WORKAROUND_BORDER,
347 image.width() - 2 * QPAINTER_WORKAROUND_BORDER,
348 image.height() - 2 * QPAINTER_WORKAROUND_BORDER);
349}
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 226 of file kis_qimage_pyramid.cpp.

229{
230 QTransform transform;
231 QSize dstSize;
232
233 calculateParams(shape, subPixelX, subPixelY,
234 originalSize,
235 &transform, &dstSize);
236
237 return dstSize;
238}

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: