the existent image data will be invalidated, so that isNull() is guaranteed
to return true in this case.
+* Behavioral change regarding QPainter fill rules when not using antialiased
+ painting: The fill rules have changed so that the aliased and antialiased
+ coordinate systems match. Earlier there used to be an offset of slightly less
+ than half a pixel when doing sub-pixel rendering, in order to be consistent
+ with the old X11 paint engine. The new behavior should be more predictable and
+ gives the same consistent rounding for images / pixmaps as for paths and
+ rectangle filling. It's possible to still get the old behavior by setting the
+ QPainter::Qt4CompatiblePainting render hint.
+
QtWidgets
---------
* QInputContext removed as well as related getters and setters on QWidget and QApplication.
if (clipLine(rx1, ry1, rx2, ry2))
return;
- const int half = 31;
+ const int half = legacyRounding ? 31 : 0;
int x1 = toF26Dot6(rx1) + half;
int y1 = toF26Dot6(ry1) + half;
int x2 = toF26Dot6(rx2) + half;
if (stroker->clipLine(rx1, ry1, rx2, ry2))
return;
- static const int half = 31;
+ const int half = stroker->legacyRounding ? 31 : 0;
int x1 = toF26Dot6(rx1) + half;
int y1 = toF26Dot6(ry1) + half;
int x2 = toF26Dot6(rx2) + half;
patternSize(0),
patternLength(0),
patternOffset(0),
+ legacyRounding(false),
current_span(0),
lastDir(LeftToRight),
lastAxisAligned(false)
{ setup(); }
+
~QCosmeticStroker() { free(pattern); free(reversePattern); }
+
+ void setLegacyRoundingEnabled(bool legacyRoundingEnabled) { legacyRounding = legacyRoundingEnabled; }
+
void drawLine(const QPointF &p1, const QPointF &p2);
void drawPath(const QVectorPath &path);
void drawPoints(const QPoint *points, int num);
int patternLength;
int patternOffset;
+ bool legacyRounding;
+
enum { NSPANS = 255 };
QT_FT_Span spans[NSPANS];
int current_span;
painter->setRenderHint(QPainter::SmoothPixmapTransform, nh & QPainter::SmoothPixmapTransform);
if (xored & QPainter::NonCosmeticDefaultPen)
painter->setRenderHint(QPainter::NonCosmeticDefaultPen, nh & QPainter::NonCosmeticDefaultPen);
+ if (xored & QPainter::Qt4CompatiblePainting)
+ painter->setRenderHint(QPainter::Qt4CompatiblePainting, nh & QPainter::Qt4CompatiblePainting);
break; }
case QPaintBufferPrivate::Cmd_SetOpacity: {
flags.fast_pen = true;
flags.antialiased = false;
flags.bilinear = false;
+ flags.legacy_rounding = false;
flags.fast_text = true;
flags.int_xform = true;
flags.tx_noshear = true;
s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing);
s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform);
+ s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting);
if (was_aa != s->flags.antialiased)
s->strokeFlags |= DirtyHints;
QRectVectorPath path;
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
for (int i = 0; i < rectCount; ++i) {
path.set(rects[i]);
stroker.drawPath(path);
QRectVectorPath path;
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
for (int i = 0; i < rectCount; ++i) {
path.set(rects[i]);
stroker.drawPath(path);
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
stroker.drawPath(path);
} else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) {
qreal width = s->lastPen.isCosmetic()
QPaintEngineEx::stroke(path, pen);
}
-static inline QRect toNormalizedFillRect(const QRectF &rect)
+QRect QRasterPaintEngine::toNormalizedFillRect(const QRectF &rect)
{
- int x1 = qRound(rect.x() + aliasedCoordinateDelta);
- int y1 = qRound(rect.y() + aliasedCoordinateDelta);
- int x2 = qRound(rect.right() + aliasedCoordinateDelta);
- int y2 = qRound(rect.bottom() + aliasedCoordinateDelta);
+ QRasterPaintEngineState *s = state();
+
+ qreal delta = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
+
+ int x1 = qRound(rect.x() + delta);
+ int y1 = qRound(rect.y() + delta);
+ int x2 = qRound(rect.right() + delta);
+ int y2 = qRound(rect.bottom() + delta);
if (x2 < x1)
qSwap(x1, x2);
QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode));
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
stroker.drawPath(vp);
} else {
QPaintEngineEx::stroke(vp, s->lastPen);
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
stroker.drawPath(vp);
} else {
QPaintEngineEx::stroke(vp, s->lastPen);
// as fillRect will apply the aliased coordinate delta we need to
// subtract it here as we don't use it for image drawing
QTransform old = s->matrix;
- s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
+
+ if (s->flags.legacy_rounding)
+ s->matrix = s->matrix * QTransform::fromTranslate(-aliasedCoordinateDelta, -aliasedCoordinateDelta);
// Do whatever fillRect() does, but without premultiplying the color if it's already premultiplied.
QRgb color = img.pixel(sr_l, sr_t);
if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
d->initializeRasterizer(&d->image_filler_xform);
d->rasterizer->setAntialiased(s->flags.antialiased);
+ d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
- const QPointF offs = s->flags.antialiased ? QPointF() : QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta);
+ const QPointF offs = s->flags.legacy_rounding ? QPointF(aliasedCoordinateDelta, aliasedCoordinateDelta) : QPointF();
const QRectF &rect = r.normalized();
const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f) - offs;
return;
}
#endif
- const qreal offs = s->flags.antialiased ? qreal(0) : aliasedCoordinateDelta;
+ const qreal offs = s->flags.legacy_rounding ? aliasedCoordinateDelta : qreal(0);
QPainterPath path;
path.addRect(r);
QTransform m = s->matrix;
if (s->flags.tx_noshear || s->matrix.type() == QTransform::TxScale) {
d->initializeRasterizer(&d->image_filler_xform);
d->rasterizer->setAntialiased(s->flags.antialiased);
+ d->rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
const QRectF &rect = r.normalized();
const QPointF a = s->matrix.map((rect.topLeft() + rect.bottomLeft()) * 0.5f);
rightShift = 3; // divide by 8
int margin = fontEngine->glyphMargin(glyphType);
- const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta);
+ const QFixed offs = s->flags.legacy_rounding ? QFixed::fromReal(aliasedCoordinateDelta) : QFixed();
const uchar *bits = image.bits();
for (int i=0; i<numGlyphs; ++i) {
}
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
stroker.drawPoints(points, pointCount);
}
}
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
stroker.drawPoints(points, pointCount);
}
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
for (int i=0; i<lineCount; ++i) {
const QLine &l = lines[i];
stroker.drawLine(l.p1(), l.p2());
return;
if (s->flags.fast_pen) {
QCosmeticStroker stroker(s, d->deviceRect);
+ stroker.setLegacyRoundingEnabled(s->flags.legacy_rounding);
for (int i=0; i<lineCount; ++i) {
QLineF line = lines[i];
stroker.drawLine(line.p1(), line.p2());
QRasterPaintEngineState *s = q->state();
rasterizer->setAntialiased(s->flags.antialiased);
+ rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
QRect clipRect(deviceRect);
ProcessSpans blend;
if (!s->flags.antialiased) {
rasterizer->setAntialiased(s->flags.antialiased);
+ rasterizer->setLegacyRoundingEnabled(s->flags.legacy_rounding);
rasterizer->setClipRect(deviceRect);
rasterizer->initialize(callback, userData);
uint non_complex_pen : 1; // can use rasterizer, rather than stroker
uint antialiased : 1;
uint bilinear : 1;
+ uint legacy_rounding : 1;
uint fast_text : 1;
uint int_xform : 1;
uint tx_noshear : 1;
bool setClipRectInDeviceCoords(const QRect &r, Qt::ClipOperation op);
+ QRect toNormalizedFillRect(const QRectF &rect);
+
inline void ensureBrush(const QBrush &brush) {
if (!qbrush_fast_equals(state()->lastBrush, brush) || (brush.style() != Qt::NoBrush && state()->fillFlags))
updateBrush(brush);
of 0 (which otherwise enables QPen::isCosmetic()) as being a non-cosmetic
pen with a width of 1.
+ \value Qt4CompatiblePainting Compatibility hint telling the engine to use the
+ same X11 based fill rules as in Qt 4, where aliased rendering is offset
+ by slightly less than half a pixel. Potentially useful when porting a
+ Qt 4 application to Qt 5.
+
\sa renderHints(), setRenderHint(), {QPainter#Rendering
Quality}{Rendering Quality}, {Concentric Circles Example}
TextAntialiasing = 0x02,
SmoothPixmapTransform = 0x04,
HighQualityAntialiasing = 0x08,
- NonCosmeticDefaultPen = 0x10
+ NonCosmeticDefaultPen = 0x10,
+ Qt4CompatiblePainting = 0x20
};
Q_DECLARE_FLAGS(RenderHints, RenderHint)
~QScanConverter();
void begin(int top, int bottom, int left, int right,
- Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
+ Qt::FillRule fillRule, bool legacyRounding, QSpanBuffer *spanBuffer);
void end();
void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b,
Q16Dot16 m_rightFP;
int m_fillRuleMask;
+ bool m_legacyRounding;
int m_x;
int m_y;
{
public:
bool antialiased;
+ bool legacyRounding;
ProcessSpans blend;
void *data;
QRect clipRect;
}
void QScanConverter::begin(int top, int bottom, int left, int right,
- Qt::FillRule fillRule, QSpanBuffer *spanBuffer)
+ Qt::FillRule fillRule, bool legacyRounding,
+ QSpanBuffer *spanBuffer)
{
m_top = top;
m_bottom = bottom;
m_lines.reset();
m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
+ m_legacyRounding = legacyRounding;
m_spanBuffer = spanBuffer;
}
winding = -1;
}
- a.x += COORD_OFFSET;
- a.y += COORD_OFFSET;
- b.x += COORD_OFFSET;
- b.y += COORD_OFFSET;
+ if (m_legacyRounding) {
+ a.x += COORD_OFFSET;
+ a.y += COORD_OFFSET;
+ b.x += COORD_OFFSET;
+ b.y += COORD_OFFSET;
+ }
+
+ int rounding = m_legacyRounding ? COORD_ROUNDING : 0;
- int iTop = qMax(m_top, int((a.y + 32 - COORD_ROUNDING) >> 6));
- int iBottom = qMin(m_bottom, int((b.y - 32 - COORD_ROUNDING) >> 6));
+ int iTop = qMax(m_top, int((a.y + 32 - rounding) >> 6));
+ int iBottom = qMin(m_bottom, int((b.y - 32 - rounding) >> 6));
if (iTop <= iBottom) {
- Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - COORD_ROUNDING;
+ Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x << 10) - rounding;
if (b.x == a.x) {
Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
QRasterizer::QRasterizer()
: d(new QRasterizerPrivate)
{
+ d->legacyRounding = false;
}
QRasterizer::~QRasterizer()
d->clipRect = clipRect;
}
+void QRasterizer::setLegacyRoundingEnabled(bool legacyRoundingEnabled)
+{
+ d->legacyRounding = legacyRoundingEnabled;
+}
+
static Q16Dot16 intersectPixelFP(int x, Q16Dot16 top, Q16Dot16 bottom, Q16Dot16 leftIntersectX, Q16Dot16 rightIntersectX, Q16Dot16 slope, Q16Dot16 invSlope)
{
Q16Dot16 leftX = IntToQ16Dot16(x);
pb = npb;
}
- if (!d->antialiased) {
+ if (!d->antialiased && d->legacyRounding) {
pa.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
pa.ry() += (COORD_OFFSET - COORD_ROUNDING)/64.;
pb.rx() += (COORD_OFFSET - COORD_ROUNDING)/64.;
max_y = qMax(p.y, max_y);
}
- int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
- int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + COORD_OFFSET - COORD_ROUNDING) >> 6));
+ int rounding = d->legacyRounding ? COORD_OFFSET - COORD_ROUNDING : 0;
+
+ int iTopBound = qMax(d->clipRect.top(), int((min_y + 32 + rounding) >> 6));
+ int iBottomBound = qMin(d->clipRect.bottom(), int((max_y - 32 + rounding) >> 6));
if (iTopBound > iBottomBound)
return;
- d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, d->legacyRounding, &buffer);
int first = 0;
for (int i = 0; i < outline->n_contours; ++i) {
QRectF bounds = path.controlPointRect();
- int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
- int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - 0.5 + (COORD_OFFSET - COORD_ROUNDING)/64.));
+ double rounding = d->legacyRounding ? (COORD_OFFSET - COORD_ROUNDING) / 64. : 0.0;
+
+ int iTopBound = qMax(d->clipRect.top(), int(bounds.top() + 0.5 + rounding));
+ int iBottomBound = qMin(d->clipRect.bottom(), int(bounds.bottom() - 0.5 + rounding));
if (iTopBound > iBottomBound)
return;
- d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, &buffer);
+ d->scanConverter.begin(iTopBound, iBottomBound, d->clipRect.left(), d->clipRect.right(), fillRule, d->legacyRounding, &buffer);
int subpathStart = 0;
QT_FT_Vector last = { 0, 0 };
void setAntialiased(bool antialiased);
void setClipRect(const QRect &clipRect);
+ void setLegacyRoundingEnabled(bool legacyRoundingEnabled);
void initialize(ProcessSpans blend, void *data);
if (opt->state & (State_Sunken | State_On)) {
ir.adjust(2, 2, -2, -2);
p->setBrush(opt->palette.foreground());
+ bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting);
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
p->drawEllipse(ir);
+ p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting);
}
break; }
case PE_FrameFocusRect:
p->translate(sx + bsx, sy + bsy);
p->setPen(opt->palette.buttonText().color());
p->setBrush(opt->palette.buttonText());
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
p->drawPolygon(a);
p->restore();
break; }
}
p->setPen(tb->palette.mid().color().darker(150));
+ bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting);
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
p->drawPolygon(a);
+ p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting);
p->setPen(tb->palette.light().color());
if (tb->direction != Qt::RightToLeft) {
p->drawLine(0, 2, tb->rect.width() - d, 2);
p->setPen(Qt::NoPen);
p->setBrush(pal.button());
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
p->drawPolygon(arrow);
a = QStyleHelper::angle(QPointF(width / 2, height / 2), arrow[0]);
imagePainter.translate(sx + bsx, sy + bsy);
imagePainter.setPen(opt->palette.buttonText().color());
imagePainter.setBrush(opt->palette.buttonText());
+ imagePainter.setRenderHint(QPainter::Qt4CompatiblePainting);
if (!(opt->state & State_Enabled)) {
imagePainter.translate(1, 1);
}
p->save();
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
bool down = opt->state & State_Sunken;
bool enabled = opt->state & State_Enabled;
bool on = opt->state & State_On;
}
QBrush oldBrush = p->brush();
+ bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting);
p->setPen(Qt::NoPen);
p->setBrush(handleBrush);
+ p->setRenderHint(QPainter::Qt4CompatiblePainting);
Qt::BGMode oldMode = p->backgroundMode();
p->setBackgroundMode(Qt::OpaqueMode);
p->drawRect(x1, y1, x2-x1+1, y2-y1+1);
p->drawLine(x2, y2-1, x2+d, y2-1-d);
break;
}
+ p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting);
}
}
break;
{ // unclipped
pixmapUnclipped.fill(Qt::white);
QPainter p(&pixmapUnclipped);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.translate(offset, offset);
p.setPen(QPen(Qt::black));
p.drawLine(line);
pixmapClipped.fill(Qt::white);
QPainter p(&pixmapClipped);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.translate(offset, offset);
p.setClipRect(clip);
p.setPen(QPen(Qt::black));
image.fill(0xffffffff);
QPainter p(&image);
p.setPen(pen);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.drawLine(QLine(0, 0+4, 0+4, 0));
p.end();
QTransform transform(0.368567, 0, 0, 0, 0.368567, 0, 0.0289, 0.0289, 1);
QPainter p(&image);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.setTransform(transform);
p.setBrush(Qt::red);
p.setPen(Qt::NoPen);
image.fill(0xffffffff);
p.begin(&image);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.setTransform(transform);
p.drawRect(QRect(14, 14, 39, 39));
p.end();
image.fill(QColor(Qt::white).rgb());
QPainter p(&image);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.setPen(Qt::NoPen);
p.setBrush(Qt::black);
p.translate(offset - expectedBounds.left(), offset - expectedBounds.top());
void tst_QPainter::drawPath3()
{
-#if !defined(Q_OS_WINCE)
- QImage imgA(400, 400, QImage::Format_RGB32);
-#else
QImage imgA(100, 100, QImage::Format_RGB32);
-#endif
imgA.fill(0xffffff);
QImage imgB = imgA;
{
pixmap.fill(Qt::white);
QPainter p(&pixmap);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.setPen(usePen ? QPen(Qt::black) : QPen(Qt::NoPen));
p.setBrush(Qt::black);
p.drawRoundRect(rect);
baseLine.fill(Qt::white);
{
QPainter p(&baseLine);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.drawText(0, fm.ascent(), QString::fromLatin1("e"));
}
{
QPainter p(&comparison);
+ p.setRenderHint(QPainter::Qt4CompatiblePainting);
p.drawText(QPointF(i / 12.0, fm.ascent()), QString::fromLatin1("e"));
}