Allow painting above INT16_MAX on 64-bit architectures

The logic in qgrayraster was ready for it except for an intermediate
format. qrasterizer.cpp and qcosmeticstroker.cpp uses a dot-16 fixed
point format, and had to be changed to handle higher coordinates on
64-bit architectures.

Fixes: QTBUG-84428
Change-Id: I85ab7a04e38bd0dbcefa9f16c16ccc84785a33cf
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Allan Sandfeld Jensen 2020-05-29 11:56:16 +02:00
parent ccc205bf38
commit 04413b9dd8
7 changed files with 266 additions and 180 deletions

View File

@ -58,6 +58,12 @@ inline QString capString(int caps)
}
#endif
#if Q_PROCESSOR_WORDSIZE == 8
typedef qint64 FDot16;
#else
typedef int FDot16;
#endif
#define toF26Dot6(x) ((int)((x)*64.))
static inline uint sourceOver(uint d, uint color)
@ -65,11 +71,15 @@ static inline uint sourceOver(uint d, uint color)
return color + BYTE_MUL(d, qAlpha(~color));
}
inline static int F16Dot16FixedDiv(int x, int y)
inline static FDot16 FDot16FixedDiv(int x, int y)
{
#if Q_PROCESSOR_WORDSIZE == 8
return FDot16(x) * (1<<16) / y;
#else
if (qAbs(x) > 0x7fff)
return qlonglong(x) * (1<<16) / y;
return x * (1<<16) / y;
#endif
}
typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
@ -157,7 +167,7 @@ inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
}
}
stroker->spans[stroker->current_span].x = ushort(x);
stroker->spans[stroker->current_span].x = x;
stroker->spans[stroker->current_span].len = 1;
stroker->spans[stroker->current_span].y = y;
stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8;
@ -441,8 +451,8 @@ void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal
qSwap(y1, y2);
qSwap(x1, x2);
}
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
int x = x1 * (1<<10);
FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
FDot16 x = FDot16(x1) * (1<<10);
int y = (y1 + 32) >> 6;
int ys = (y2 + 32) >> 6;
@ -473,8 +483,8 @@ void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal
qSwap(x1, x2);
qSwap(y1, y2);
}
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
int y = y1 * (1 << 10);
FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
FDot16 y = FDot16(y1) * (1 << 10);
int x = (x1 + 32) >> 6;
int xs = (x2 + 32) >> 6;
@ -715,7 +725,7 @@ static inline int swapCaps(int caps)
}
// adjust line by half a pixel
static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc)
static inline void capAdjust(int caps, int &x1, int &x2, FDot16 &y, FDot16 yinc)
{
if (caps & QCosmeticStroker::CapBegin) {
x1 -= 32;
@ -763,8 +773,8 @@ static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2,
caps = swapCaps(caps);
dir = QCosmeticStroker::BottomToTop;
}
int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1);
int x = x1 * (1<<10);
FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
FDot16 x = FDot16(x1) * (1<<10);
if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
@ -853,8 +863,8 @@ static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2,
caps = swapCaps(caps);
dir = QCosmeticStroker::RightToLeft;
}
int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1);
int y = y1 * (1<<10);
FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
FDot16 y = FDot16(y1) * (1<<10);
if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
@ -950,7 +960,7 @@ static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx
if (qAbs(dx) < qAbs(dy)) {
// vertical
int xinc = F16Dot16FixedDiv(dx, dy);
FDot16 xinc = FDot16FixedDiv(dx, dy);
bool swapped = false;
if (y1 > y2) {
@ -960,7 +970,7 @@ static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx
caps = swapCaps(caps);
}
int x = (x1 - 32) * (1<<10);
FDot16 x = FDot16(x1 - 32) * (1<<10);
x -= ( ((y1 & 63) - 32) * xinc ) >> 6;
capAdjust(caps, y1, y2, x, xinc);
@ -1013,7 +1023,7 @@ static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx
if (!dx)
return true;
int yinc = F16Dot16FixedDiv(dy, dx);
FDot16 yinc = FDot16FixedDiv(dy, dx);
bool swapped = false;
if (x1 > x2) {
@ -1023,7 +1033,7 @@ static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx
caps = swapCaps(caps);
}
int y = (y1 - 32) * (1<<10);
FDot16 y = FDot16(y1 - 32) * (1<<10);
y -= ( ((x1 & 63) - 32) * yinc ) >> 6;
capAdjust(caps, x1, x2, y, yinc);

View File

@ -1101,7 +1101,7 @@ QT_FT_END_STMNT
L = QT_FT_HYPOT( dx_, dy_ );
/* Avoid possible arithmetic overflow below by splitting. */
if ( L > 32767 )
if ( L >= (1 << 23) )
goto Split;
/* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
@ -1251,13 +1251,13 @@ QT_FT_END_STMNT
y += (TCoord)ras.min_ey;
x += (TCoord)ras.min_ex;
/* QT_FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
if ( x >= 32767 )
x = 32767;
/* QT_FT_Span.x is an int, so limit our coordinates appropriately */
if ( x >= (1 << 23) )
x = (1 << 23) - 1;
/* QT_FT_Span.y is a 16-bit short, so limit our coordinates appropriately */
if ( y >= 32767 )
y = 32767;
/* QT_FT_Span.y is an int, so limit our coordinates appropriately */
if ( y >= (1 << 23) )
y = (1 << 23) - 1;
if ( coverage )
{
@ -1271,10 +1271,10 @@ QT_FT_END_STMNT
span = ras.gray_spans + count - 1;
if ( count > 0 &&
span->y == y &&
(int)span->x + span->len == (int)x &&
span->x + span->len == x &&
span->coverage == coverage )
{
span->len = (unsigned short)( span->len + acount );
span->len = span->len + acount;
return;
}
@ -1317,9 +1317,9 @@ QT_FT_END_STMNT
span++;
/* add a gray span to the current list */
span->x = (short)x;
span->len = (unsigned short)acount;
span->y = (short)y;
span->x = x;
span->len = acount;
span->y = y;
span->coverage = (unsigned char)coverage;
ras.num_gray_spans++;
@ -1895,10 +1895,10 @@ QT_FT_END_STMNT
}
else
{
ras.clip_box.xMin = -32768L;
ras.clip_box.yMin = -32768L;
ras.clip_box.xMax = 32767L;
ras.clip_box.yMax = 32767L;
ras.clip_box.xMin = -(1 << 23);
ras.clip_box.yMin = -(1 << 23);
ras.clip_box.xMax = (1 << 23) - 1;
ras.clip_box.yMax = (1 << 23) - 1;
}
gray_init_cells( worker, raster->buffer, raster->buffer_size );

View File

@ -66,10 +66,13 @@
QT_BEGIN_NAMESPACE
// This limitations comes from qgrayraster.c. Any higher and
// rasterization of shapes will produce incorrect results.
const int QT_RASTER_COORD_LIMIT = 32767;
// These limitations comes from qrasterizer.cpp, qcosmeticstroker.cpp, and qgrayraster.c.
// Any higher and rasterization of shapes will produce incorrect results.
#if Q_PROCESSOR_WORDSIZE == 8
constexpr int QT_RASTER_COORD_LIMIT = ((1<<23) - 1); // F24dot8 in qgrayraster.c
#else
constexpr int QT_RASTER_COORD_LIMIT = ((1<<15) - 1); // F16dot16 in qrasterizer.cpp and qcosmeticstroker.cpp
#endif
//#define QT_DEBUG_CONVERT
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);

View File

@ -4142,10 +4142,10 @@ static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userDa
static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
const QRect &clip)
{
const short minx = clip.left();
const short miny = clip.top();
const short maxx = clip.right();
const short maxy = clip.bottom();
const int minx = clip.left();
const int miny = clip.top();
const int maxx = clip.right();
const int maxy = clip.bottom();
QT_FT_Span *end = spans + numSpans;
while (spans < end) {
@ -4167,7 +4167,7 @@ static int qt_intersect_spans(QT_FT_Span *&spans, int numSpans,
s->len = qMin(s->len - (minx - s->x), maxx - minx + 1);
s->x = minx;
} else {
s->len = qMin(s->len, ushort(maxx - s->x + 1));
s->len = qMin(s->len, (maxx - s->x + 1));
}
++s;
}

View File

@ -865,10 +865,10 @@ QT_FT_BEGIN_HEADER
/* */
typedef struct QT_FT_Span_
{
short x;
unsigned short len;
short y;
unsigned char coverage;
int x;
int len;
int y;
unsigned char coverage;
} QT_FT_Span;

View File

@ -52,15 +52,20 @@
QT_BEGIN_NAMESPACE
typedef int Q16Dot16;
#define Q16Dot16ToFloat(i) ((i)/65536.)
#define FloatToQ16Dot16(i) (int)((i) * 65536.)
#define IntToQ16Dot16(i) ((i) * (1 << 16))
#define Q16Dot16ToInt(i) ((i) >> 16)
#define Q16Dot16Factor 65536
#if Q_PROCESSOR_WORDSIZE == 8
typedef qint64 QScFixed;
#else
typedef int QScFixed;
#endif
#define QScFixedToFloat(i) ((i) * (1./65536.))
#define FloatToQScFixed(i) (QScFixed)((i) * 65536.)
#define IntToQScFixed(i) ((QScFixed)(i) * (1 << 16))
#define QScFixedToInt(i) ((i) >> 16)
#define QScFixedFactor 65536
#define FTPosToQScFixed(i) ((QScFixed)(i) * (1 << 10))
#define Q16Dot16Multiply(x, y) (int)((qlonglong(x) * qlonglong(y)) >> 16)
#define Q16Dot16FastMultiply(x, y) (((x) * (y)) >> 16)
#define QScFixedMultiply(x, y) (QScFixed)((qlonglong(x) * qlonglong(y)) >> 16)
#define QScFixedFastMultiply(x, y) (((x) * (y)) >> 16)
#define SPAN_BUFFER_SIZE 256
@ -88,11 +93,12 @@ public:
flushSpans();
}
void addSpan(int x, unsigned int len, int y, unsigned char coverage)
void addSpan(int x, int len, int y, int coverage)
{
if (!coverage || !len)
return;
Q_ASSERT(coverage >= 0 && coverage <= 255);
Q_ASSERT(y >= m_clipRect.top());
Q_ASSERT(y <= m_clipRect.bottom());
Q_ASSERT(x >= m_clipRect.left());
@ -140,8 +146,8 @@ public:
struct Line
{
Q16Dot16 x;
Q16Dot16 delta;
QScFixed x;
QScFixed delta;
int top, bottom;
@ -157,7 +163,7 @@ private:
int left, right;
};
inline bool clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding);
inline bool clip(QScFixed &xFP, int &iTop, int &iBottom, QScFixed slopeFP, QScFixed edgeFP, int winding);
inline void mergeIntersection(Intersection *head, const Intersection &isect);
void prepareChunk();
@ -175,8 +181,8 @@ private:
int m_top;
int m_bottom;
Q16Dot16 m_leftFP;
Q16Dot16 m_rightFP;
QScFixed m_leftFP;
QScFixed m_rightFP;
int m_fillRuleMask;
bool m_legacyRounding;
@ -228,8 +234,8 @@ void QScanConverter::begin(int top, int bottom, int left, int right,
{
m_top = top;
m_bottom = bottom;
m_leftFP = IntToQ16Dot16(left);
m_rightFP = IntToQ16Dot16(right + 1);
m_leftFP = IntToQScFixed(left);
m_rightFP = IntToQScFixed(right + 1);
m_lines.reset();
@ -350,7 +356,7 @@ void QScanConverter::scanConvert()
for (int i = 0; i < numActive; ++i) {
QScanConverter::Line *node = m_active.at(i);
const int current = Q16Dot16ToInt(node->x);
const int current = QScFixedToInt(node->x);
if (winding & m_fillRuleMask)
m_spanBuffer->addSpan(x, current - x, y, 0xff);
@ -414,12 +420,12 @@ void QScanConverter::end()
if (line.delta) {
for (; it != end; ++it) {
isect.x = Q16Dot16ToInt(line.x);
isect.x = QScFixedToInt(line.x);
line.x += line.delta;
mergeIntersection(it, isect);
}
} else {
isect.x = Q16Dot16ToInt(line.x);
isect.x = QScFixedToInt(line.x);
for (; it != end; ++it)
mergeIntersection(it, isect);
}
@ -516,7 +522,7 @@ void QScanConverter::mergeCurve(const QT_FT_Vector &pa, const QT_FT_Vector &pb,
}
}
inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot16 slopeFP, Q16Dot16 edgeFP, int winding)
inline bool QScanConverter::clip(QScFixed &xFP, int &iTop, int &iBottom, QScFixed slopeFP, QScFixed edgeFP, int winding)
{
bool right = edgeFP == m_rightFP;
@ -530,7 +536,7 @@ inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot1
}
}
Q16Dot16 lastFP = xFP + slopeFP * (iBottom - iTop);
QScFixed lastFP = xFP + slopeFP * (iBottom - iTop);
if (lastFP == edgeFP) {
if ((slopeFP < 0) ^ right)
@ -544,11 +550,11 @@ inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot1
// does line cross edge?
if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
Q16Dot16 deltaY = Q16Dot16((edgeFP - xFP) / Q16Dot16ToFloat(slopeFP));
QScFixed deltaY = QScFixed((edgeFP - xFP) / QScFixedToFloat(slopeFP));
if ((xFP < edgeFP) ^ right) {
// top segment needs to be clipped
int iHeight = Q16Dot16ToInt(deltaY + 1);
int iHeight = QScFixedToInt(deltaY + 1);
int iMiddle = iTop + iHeight;
Line line = { edgeFP, 0, iTop, iMiddle, winding };
@ -561,7 +567,7 @@ inline bool QScanConverter::clip(Q16Dot16 &xFP, int &iTop, int &iBottom, Q16Dot1
return true;
} else {
// bottom segment needs to be clipped
int iHeight = Q16Dot16ToInt(deltaY);
int iHeight = QScFixedToInt(deltaY);
int iMiddle = iTop + iHeight;
if (iMiddle != iBottom) {
@ -603,7 +609,7 @@ void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
int iBottom = qMin(m_bottom, int((b.y - 32 - rounding) >> 6));
if (iTop <= iBottom) {
Q16Dot16 aFP = Q16Dot16Factor/2 + (a.x * (1 << 10)) - rounding;
QScFixed aFP = QScFixedFactor/2 + FTPosToQScFixed(a.x - rounding);
if (b.x == a.x) {
Line line = { qBound(m_leftFP, aFP, m_rightFP), 0, iTop, iBottom, winding };
@ -611,11 +617,11 @@ void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
} else {
const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
const Q16Dot16 slopeFP = FloatToQ16Dot16(slope);
const QScFixed slopeFP = FloatToQScFixed(slope);
Q16Dot16 xFP = aFP + Q16Dot16Multiply(slopeFP,
IntToQ16Dot16(iTop)
+ Q16Dot16Factor/2 - (a.y * (1 << 10)));
QScFixed xFP = aFP + QScFixedMultiply(slopeFP,
IntToQScFixed(iTop)
+ QScFixedFactor/2 - FTPosToQScFixed(a.y));
if (clip(xFP, iTop, iBottom, slopeFP, m_leftFP, winding))
return;
@ -663,37 +669,37 @@ 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)
static QScFixed intersectPixelFP(int x, QScFixed top, QScFixed bottom, QScFixed leftIntersectX, QScFixed rightIntersectX, QScFixed slope, QScFixed invSlope)
{
Q16Dot16 leftX = IntToQ16Dot16(x);
Q16Dot16 rightX = IntToQ16Dot16(x) + Q16Dot16Factor;
QScFixed leftX = IntToQScFixed(x);
QScFixed rightX = IntToQScFixed(x) + QScFixedFactor;
Q16Dot16 leftIntersectY, rightIntersectY;
QScFixed leftIntersectY, rightIntersectY;
if (slope > 0) {
leftIntersectY = top + Q16Dot16Multiply(leftX - leftIntersectX, invSlope);
leftIntersectY = top + QScFixedMultiply(leftX - leftIntersectX, invSlope);
rightIntersectY = leftIntersectY + invSlope;
} else {
leftIntersectY = top + Q16Dot16Multiply(leftX - rightIntersectX, invSlope);
leftIntersectY = top + QScFixedMultiply(leftX - rightIntersectX, invSlope);
rightIntersectY = leftIntersectY + invSlope;
}
if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
return Q16Dot16Multiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
return QScFixedMultiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
} else if (leftIntersectX >= rightX) {
return bottom - top;
} else if (leftIntersectX >= leftX) {
if (slope > 0) {
return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
return (bottom - top) - QScFixedFastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
} else {
return (bottom - top) - Q16Dot16FastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
return (bottom - top) - QScFixedFastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
}
} else if (rightIntersectX <= leftX) {
return 0;
} else if (rightIntersectX <= rightX) {
if (slope > 0) {
return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
return QScFixedFastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
} else {
return Q16Dot16FastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
return QScFixedFastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
}
} else {
if (slope > 0) {
@ -734,14 +740,14 @@ static inline qreal qSafeDivide(qreal x, qreal y)
too small to fit into INT_MIN, so we need this slightly safer conversion
when floating point exceptions are enabled
*/
static inline int qSafeFloatToQ16Dot16(qreal x)
static inline QScFixed qSafeFloatToQScFixed(qreal x)
{
qreal tmp = x * 65536.;
if (tmp > qreal(INT_MAX))
return INT_MAX;
else if (tmp < qreal(INT_MIN))
return -INT_MAX;
return int(tmp);
qreal tmp = x * QScFixedFactor;
if (tmp > qreal(std::numeric_limits<QScFixed>::max()))
return std::numeric_limits<QScFixed>::max();
else if (tmp < qreal(std::numeric_limits<QScFixed>::min()))
return std::numeric_limits<QScFixed>::min();
return QScFixed(tmp);
}
void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
@ -855,30 +861,33 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
return;
if (d->antialiased) {
const Q16Dot16 iLeft = int(left);
const Q16Dot16 iRight = int(right);
const Q16Dot16 leftWidth = IntToQ16Dot16(iLeft + 1)
- qSafeFloatToQ16Dot16(left);
const Q16Dot16 rightWidth = qSafeFloatToQ16Dot16(right)
- IntToQ16Dot16(iRight);
const int iLeft = int(left);
const int iRight = int(right);
const QScFixed leftWidth = IntToQScFixed(iLeft + 1)
- qSafeFloatToQScFixed(left);
const QScFixed rightWidth = qSafeFloatToQScFixed(right)
- IntToQScFixed(iRight);
Q16Dot16 coverage[3];
QScFixed coverage[3];
int x[3];
int len[3];
int n = 1;
if (iLeft == iRight) {
coverage[0] = (leftWidth + rightWidth) * 255;
if (leftWidth == QScFixedFactor)
coverage[0] = rightWidth * 255;
else
coverage[0] = (leftWidth + rightWidth) * 255;
x[0] = iLeft;
len[0] = 1;
} else {
coverage[0] = leftWidth * 255;
x[0] = iLeft;
len[0] = 1;
if (leftWidth == Q16Dot16Factor) {
if (leftWidth == QScFixedFactor) {
len[0] = iRight - iLeft;
} else if (iRight - iLeft > 1) {
coverage[1] = IntToQ16Dot16(255);
coverage[1] = IntToQScFixed(255);
x[1] = iLeft + 1;
len[1] = iRight - iLeft - 1;
++n;
@ -891,19 +900,19 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
}
}
const Q16Dot16 iTopFP = IntToQ16Dot16(int(pa.y()));
const Q16Dot16 iBottomFP = IntToQ16Dot16(int(pb.y()));
const Q16Dot16 yPa = qSafeFloatToQ16Dot16(pa.y());
const Q16Dot16 yPb = qSafeFloatToQ16Dot16(pb.y());
for (Q16Dot16 yFP = iTopFP; yFP <= iBottomFP; yFP += Q16Dot16Factor) {
const Q16Dot16 rowHeight = qMin(yFP + Q16Dot16Factor, yPb)
const QScFixed iTopFP = IntToQScFixed(int(pa.y()));
const QScFixed iBottomFP = IntToQScFixed(int(pb.y()));
const QScFixed yPa = qSafeFloatToQScFixed(pa.y());
const QScFixed yPb = qSafeFloatToQScFixed(pb.y());
for (QScFixed yFP = iTopFP; yFP <= iBottomFP; yFP += QScFixedFactor) {
const QScFixed rowHeight = qMin(yFP + QScFixedFactor, yPb)
- qMax(yFP, yPa);
const int y = Q16Dot16ToInt(yFP);
const int y = QScFixedToInt(yFP);
if (y > d->clipRect.bottom())
break;
for (int i = 0; i < n; ++i) {
buffer.addSpan(x[i], len[i], y,
Q16Dot16ToInt(Q16Dot16Multiply(rowHeight, coverage[i])));
QScFixedToInt(QScFixedMultiply(rowHeight, coverage[i])));
}
}
} else { // aliased
@ -960,111 +969,111 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
const qreal topRightSlope = qSafeDivide(topRightEdge.x(), topRightEdge.y());
const qreal bottomRightSlope = qSafeDivide(bottomRightEdge.x(), bottomRightEdge.y());
const Q16Dot16 topLeftSlopeFP = qSafeFloatToQ16Dot16(topLeftSlope);
const Q16Dot16 topRightSlopeFP = qSafeFloatToQ16Dot16(topRightSlope);
const QScFixed topLeftSlopeFP = qSafeFloatToQScFixed(topLeftSlope);
const QScFixed topRightSlopeFP = qSafeFloatToQScFixed(topRightSlope);
const Q16Dot16 bottomLeftSlopeFP = qSafeFloatToQ16Dot16(bottomLeftSlope);
const Q16Dot16 bottomRightSlopeFP = qSafeFloatToQ16Dot16(bottomRightSlope);
const QScFixed bottomLeftSlopeFP = qSafeFloatToQScFixed(bottomLeftSlope);
const QScFixed bottomRightSlopeFP = qSafeFloatToQScFixed(bottomRightSlope);
const Q16Dot16 invTopLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topLeftSlope));
const Q16Dot16 invTopRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, topRightSlope));
const QScFixed invTopLeftSlopeFP = qSafeFloatToQScFixed(qSafeDivide(1, topLeftSlope));
const QScFixed invTopRightSlopeFP = qSafeFloatToQScFixed(qSafeDivide(1, topRightSlope));
const Q16Dot16 invBottomLeftSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomLeftSlope));
const Q16Dot16 invBottomRightSlopeFP = qSafeFloatToQ16Dot16(qSafeDivide(1, bottomRightSlope));
const QScFixed invBottomLeftSlopeFP = qSafeFloatToQScFixed(qSafeDivide(1, bottomLeftSlope));
const QScFixed invBottomRightSlopeFP = qSafeFloatToQScFixed(qSafeDivide(1, bottomRightSlope));
if (d->antialiased) {
const Q16Dot16 iTopFP = IntToQ16Dot16(int(topBound));
const Q16Dot16 iLeftFP = IntToQ16Dot16(int(left.y()));
const Q16Dot16 iRightFP = IntToQ16Dot16(int(right.y()));
const Q16Dot16 iBottomFP = IntToQ16Dot16(int(bottomBound));
const QScFixed iTopFP = IntToQScFixed(int(topBound));
const QScFixed iLeftFP = IntToQScFixed(int(left.y()));
const QScFixed iRightFP = IntToQScFixed(int(right.y()));
const QScFixed iBottomFP = IntToQScFixed(int(bottomBound));
Q16Dot16 leftIntersectAf = qSafeFloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topLeftSlope);
Q16Dot16 rightIntersectAf = qSafeFloatToQ16Dot16(top.x() + (int(topBound) - top.y()) * topRightSlope);
Q16Dot16 leftIntersectBf = 0;
Q16Dot16 rightIntersectBf = 0;
QScFixed leftIntersectAf = qSafeFloatToQScFixed(top.x() + (int(topBound) - top.y()) * topLeftSlope);
QScFixed rightIntersectAf = qSafeFloatToQScFixed(top.x() + (int(topBound) - top.y()) * topRightSlope);
QScFixed leftIntersectBf = 0;
QScFixed rightIntersectBf = 0;
if (iLeftFP < iTopFP)
leftIntersectBf = qSafeFloatToQ16Dot16(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
leftIntersectBf = qSafeFloatToQScFixed(left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
if (iRightFP < iTopFP)
rightIntersectBf = qSafeFloatToQ16Dot16(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
rightIntersectBf = qSafeFloatToQScFixed(right.x() + (int(topBound) - right.y()) * bottomRightSlope);
Q16Dot16 rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
Q16Dot16 topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
Q16Dot16 bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
QScFixed rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
QScFixed topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
QScFixed bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
int leftMin, leftMax, rightMin, rightMax;
const Q16Dot16 yTopFP = qSafeFloatToQ16Dot16(top.y());
const Q16Dot16 yLeftFP = qSafeFloatToQ16Dot16(left.y());
const Q16Dot16 yRightFP = qSafeFloatToQ16Dot16(right.y());
const Q16Dot16 yBottomFP = qSafeFloatToQ16Dot16(bottom.y());
const QScFixed yTopFP = qSafeFloatToQScFixed(top.y());
const QScFixed yLeftFP = qSafeFloatToQScFixed(left.y());
const QScFixed yRightFP = qSafeFloatToQScFixed(right.y());
const QScFixed yBottomFP = qSafeFloatToQScFixed(bottom.y());
rowTop = qMax(iTopFP, yTopFP);
topLeftIntersectAf = leftIntersectAf +
Q16Dot16Multiply(topLeftSlopeFP, rowTop - iTopFP);
QScFixedMultiply(topLeftSlopeFP, rowTop - iTopFP);
topRightIntersectAf = rightIntersectAf +
Q16Dot16Multiply(topRightSlopeFP, rowTop - iTopFP);
QScFixedMultiply(topRightSlopeFP, rowTop - iTopFP);
Q16Dot16 yFP = iTopFP;
QScFixed yFP = iTopFP;
while (yFP <= iBottomFP) {
rowBottomLeft = qMin(yFP + Q16Dot16Factor, yLeftFP);
rowBottomRight = qMin(yFP + Q16Dot16Factor, yRightFP);
rowBottomLeft = qMin(yFP + QScFixedFactor, yLeftFP);
rowBottomRight = qMin(yFP + QScFixedFactor, yRightFP);
rowTopLeft = qMax(yFP, yLeftFP);
rowTopRight = qMax(yFP, yRightFP);
rowBottom = qMin(yFP + Q16Dot16Factor, yBottomFP);
rowBottom = qMin(yFP + QScFixedFactor, yBottomFP);
if (yFP == iLeftFP) {
const int y = Q16Dot16ToInt(yFP);
leftIntersectBf = qSafeFloatToQ16Dot16(left.x() + (y - left.y()) * bottomLeftSlope);
topLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowTopLeft - yFP);
bottomLeftIntersectAf = leftIntersectAf + Q16Dot16Multiply(topLeftSlopeFP, rowBottomLeft - yFP);
const int y = QScFixedToInt(yFP);
leftIntersectBf = qSafeFloatToQScFixed(left.x() + (y - left.y()) * bottomLeftSlope);
topLeftIntersectBf = leftIntersectBf + QScFixedMultiply(bottomLeftSlopeFP, rowTopLeft - yFP);
bottomLeftIntersectAf = leftIntersectAf + QScFixedMultiply(topLeftSlopeFP, rowBottomLeft - yFP);
} else {
topLeftIntersectBf = leftIntersectBf;
bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
}
if (yFP == iRightFP) {
const int y = Q16Dot16ToInt(yFP);
rightIntersectBf = qSafeFloatToQ16Dot16(right.x() + (y - right.y()) * bottomRightSlope);
topRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowTopRight - yFP);
bottomRightIntersectAf = rightIntersectAf + Q16Dot16Multiply(topRightSlopeFP, rowBottomRight - yFP);
const int y = QScFixedToInt(yFP);
rightIntersectBf = qSafeFloatToQScFixed(right.x() + (y - right.y()) * bottomRightSlope);
topRightIntersectBf = rightIntersectBf + QScFixedMultiply(bottomRightSlopeFP, rowTopRight - yFP);
bottomRightIntersectAf = rightIntersectAf + QScFixedMultiply(topRightSlopeFP, rowBottomRight - yFP);
} else {
topRightIntersectBf = rightIntersectBf;
bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
}
if (yFP == iBottomFP) {
bottomLeftIntersectBf = leftIntersectBf + Q16Dot16Multiply(bottomLeftSlopeFP, rowBottom - yFP);
bottomRightIntersectBf = rightIntersectBf + Q16Dot16Multiply(bottomRightSlopeFP, rowBottom - yFP);
bottomLeftIntersectBf = leftIntersectBf + QScFixedMultiply(bottomLeftSlopeFP, rowBottom - yFP);
bottomRightIntersectBf = rightIntersectBf + QScFixedMultiply(bottomRightSlopeFP, rowBottom - yFP);
} else {
bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
}
if (yFP < iLeftFP) {
leftMin = Q16Dot16ToInt(bottomLeftIntersectAf);
leftMax = Q16Dot16ToInt(topLeftIntersectAf);
leftMin = QScFixedToInt(bottomLeftIntersectAf);
leftMax = QScFixedToInt(topLeftIntersectAf);
} else if (yFP == iLeftFP) {
leftMin = Q16Dot16ToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
leftMax = Q16Dot16ToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
leftMin = QScFixedToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
leftMax = QScFixedToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
} else {
leftMin = Q16Dot16ToInt(topLeftIntersectBf);
leftMax = Q16Dot16ToInt(bottomLeftIntersectBf);
leftMin = QScFixedToInt(topLeftIntersectBf);
leftMax = QScFixedToInt(bottomLeftIntersectBf);
}
leftMin = qBound(d->clipRect.left(), leftMin, d->clipRect.right());
leftMax = qBound(d->clipRect.left(), leftMax, d->clipRect.right());
if (yFP < iRightFP) {
rightMin = Q16Dot16ToInt(topRightIntersectAf);
rightMax = Q16Dot16ToInt(bottomRightIntersectAf);
rightMin = QScFixedToInt(topRightIntersectAf);
rightMax = QScFixedToInt(bottomRightIntersectAf);
} else if (yFP == iRightFP) {
rightMin = Q16Dot16ToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
rightMax = Q16Dot16ToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
rightMin = QScFixedToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
rightMax = QScFixedToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
} else {
rightMin = Q16Dot16ToInt(bottomRightIntersectBf);
rightMax = Q16Dot16ToInt(topRightIntersectBf);
rightMin = QScFixedToInt(bottomRightIntersectBf);
rightMax = QScFixedToInt(topRightIntersectBf);
}
rightMin = qBound(d->clipRect.left(), rightMin, d->clipRect.right());
@ -1075,11 +1084,11 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
if (rightMin < leftMin)
rightMin = leftMin;
Q16Dot16 rowHeight = rowBottom - rowTop;
QScFixed rowHeight = rowBottom - rowTop;
int x = leftMin;
while (x <= leftMax) {
Q16Dot16 excluded = 0;
QScFixed excluded = 0;
if (yFP <= iLeftFP)
excluded += intersectPixelFP(x, rowTop, rowBottomLeft,
@ -1101,18 +1110,18 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
bottomRightSlopeFP, invBottomRightSlopeFP);
}
Q16Dot16 coverage = rowHeight - excluded;
buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
Q16Dot16ToInt(255 * coverage));
QScFixed coverage = rowHeight - excluded;
buffer.addSpan(x, 1, QScFixedToInt(yFP),
QScFixedToInt(255 * coverage));
++x;
}
if (x < rightMin) {
buffer.addSpan(x, rightMin - x, Q16Dot16ToInt(yFP),
Q16Dot16ToInt(255 * rowHeight));
buffer.addSpan(x, rightMin - x, QScFixedToInt(yFP),
QScFixedToInt(255 * rowHeight));
x = rightMin;
}
while (x <= rightMax) {
Q16Dot16 excluded = 0;
QScFixed excluded = 0;
if (yFP <= iRightFP)
excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, rowTop, rowBottomRight,
topRightIntersectAf, bottomRightIntersectAf,
@ -1122,9 +1131,9 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
bottomRightIntersectBf, topRightIntersectBf,
bottomRightSlopeFP, invBottomRightSlopeFP);
Q16Dot16 coverage = rowHeight - excluded;
buffer.addSpan(x, 1, Q16Dot16ToInt(yFP),
Q16Dot16ToInt(255 * coverage));
QScFixed coverage = rowHeight - excluded;
buffer.addSpan(x, 1, QScFixedToInt(yFP),
QScFixedToInt(255 * coverage));
++x;
}
@ -1135,7 +1144,7 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
topLeftIntersectAf = leftIntersectAf;
topRightIntersectAf = rightIntersectAf;
yFP += Q16Dot16Factor;
yFP += QScFixedFactor;
rowTop = yFP;
}
} else { // aliased
@ -1145,10 +1154,10 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
int iMiddle = qMin(iLeft, iRight);
Q16Dot16 leftIntersectAf = qSafeFloatToQ16Dot16(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
Q16Dot16 leftIntersectBf = qSafeFloatToQ16Dot16(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
Q16Dot16 rightIntersectAf = qSafeFloatToQ16Dot16(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
Q16Dot16 rightIntersectBf = qSafeFloatToQ16Dot16(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
QScFixed leftIntersectAf = qSafeFloatToQScFixed(top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
QScFixed leftIntersectBf = qSafeFloatToQScFixed(left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
QScFixed rightIntersectAf = qSafeFloatToQScFixed(top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
QScFixed rightIntersectBf = qSafeFloatToQScFixed(right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
int ny;
int y = iTop;
@ -1162,8 +1171,8 @@ void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width,
if (next > d->clipRect.bottom()) \
next = d->clipRect.bottom(); \
for (; y <= next; ++y) { \
const int x1 = qMax(Q16Dot16ToInt(li), d->clipRect.left()); \
const int x2 = qMin(Q16Dot16ToInt(ri), d->clipRect.right()); \
const int x1 = qMax(QScFixedToInt(li), d->clipRect.left()); \
const int x2 = qMin(QScFixedToInt(ri), d->clipRect.right()); \
if (x2 >= x1) \
buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
li += ls; \

View File

@ -240,6 +240,9 @@ private slots:
void wideImage();
void largeFillScale();
void largeRasterScale();
#if defined(Q_OS_WIN)
void toWinHBITMAP_data();
void toWinHBITMAP();
@ -3837,6 +3840,67 @@ void tst_QImage::wideImage()
// Qt6: Test that it actually works on 64bit architectures.
}
void tst_QImage::largeFillScale()
{
#if Q_PROCESSOR_WORDSIZE < 8
QSKIP("Test fails on 32-bit builds");
#endif
// Test from QTBUG-84428
QImage input(QSize(std::numeric_limits<qint16>::max() + 10, 1), QImage::Format_ARGB32_Premultiplied);
input.fill(Qt::white);
const int scaleFactor = 2;
QImage scaled = input.scaled(input.width(), input.height() * scaleFactor);
for (int x = 0, w = input.width(); x < w; ++x) {
const auto inputPixel = input.pixel(x, 0);
auto scaledPixel = scaled.pixel(x, 0);
QCOMPARE(scaledPixel, inputPixel);
scaledPixel = scaled.pixel(x, 1);
QCOMPARE(scaledPixel, inputPixel);
}
}
void tst_QImage::largeRasterScale()
{
#if Q_PROCESSOR_WORDSIZE < 8
QSKIP("Test fails on 32-bit builds");
#endif
// Now test that qgrayraster still works at these ranges
QImage image(QSize(40000, 200), QImage::Format_RGB32);
image.fill(Qt::white);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(Qt::black);
painter.drawEllipse(QPoint(33000, 100), 6990, 99);
painter.end();
QCOMPARE(image.pixelColor(27000, 10), Qt::white);
QCOMPARE(image.pixelColor(33000, 10), Qt::black);
QCOMPARE(image.pixelColor(39000, 10), Qt::white);
QCOMPARE(image.pixelColor(27000, 100), Qt::black);
QCOMPARE(image.pixelColor(33000, 100), Qt::black);
QCOMPARE(image.pixelColor(39000, 100), Qt::black);
QCOMPARE(image.pixelColor(27000, 190), Qt::white);
QCOMPARE(image.pixelColor(33000, 190), Qt::black);
QCOMPARE(image.pixelColor(39000, 190), Qt::white);
// Now check grayscale antialiasing takes place in the higher coords
bool grayObserved = false;
for (int x = 33000; x < 39000; ++x) {
QRgb pixel = image.pixel(x, 20);
if (pixel == 0xff000000)
continue; // still black
if (pixel == 0xffffffff) {
QVERIFY(grayObserved);
break;
}
grayObserved = true;
}
// image.save("largeRasterScale.png", "PNG");
}
#if defined(Q_OS_WIN)
static inline QColor COLORREFToQColor(COLORREF cr)