diff options
-rw-r--r-- | src/gui/image/qimage.cpp | 113 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 13 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 21 | ||||
-rw-r--r-- | tests/auto/gui/painting/qpainter/tst_qpainter.cpp | 103 |
4 files changed, 206 insertions, 44 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 224043ca79..b35012d4fe 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -476,6 +476,10 @@ bool QImageData::checkForAlphaPixels() const \snippet code/src_gui_image_qimage.cpp 1 \endtable + For images with more than 8-bit per color-channel. The methods + setPixelColor() and pixelColor() can be used to set and get + with QColor values. + QImage also provide the scanLine() function which returns a pointer to the pixel data at the scanline with the given index, and the bits() function which returns a pointer to the first pixel @@ -2187,9 +2191,10 @@ int QImage::pixelIndex(int x, int y) const If the \a position is not valid, the results are undefined. \warning This function is expensive when used for massive pixel - manipulations. + manipulations. Use constBits() or constScanLine() when many + pixels needs to be read. - \sa setPixel(), valid(), {QImage#Pixel Manipulation}{Pixel + \sa setPixel(), valid(), constBits(), constScanLine(), {QImage#Pixel Manipulation}{Pixel Manipulation} */ @@ -2239,25 +2244,23 @@ QRgb QImage::pixel(int x, int y) const return *layout->convertToARGB32PM(&result, ptr, 1, layout, 0); } - /*! \fn void QImage::setPixel(const QPoint &position, uint index_or_rgb) Sets the pixel index or color at the given \a position to \a index_or_rgb. - If the image's format is either monochrome or 8-bit, the given \a + If the image's format is either monochrome or paletted, the given \a index_or_rgb value must be an index in the image's color table, otherwise the parameter must be a QRgb value. If \a position is not a valid coordinate pair in the image, or if \a index_or_rgb >= colorCount() in the case of monochrome and - 8-bit images, the result is undefined. + paletted images, the result is undefined. \warning This function is expensive due to the call of the internal \c{detach()} function called within; if performance is a concern, we - recommend the use of \l{QImage::}{scanLine()} to access pixel data - directly. + recommend the use of scanLine() or bits() to access pixel data directly. \sa pixel(), {QImage#Pixel Manipulation}{Pixel Manipulation} */ @@ -2345,6 +2348,102 @@ void QImage::setPixel(int x, int y, uint index_or_rgb) } /*! + \fn QColor QImage::pixelColor(const QPoint &position) const + \since 5.6 + + Returns the color of the pixel at the given \a position as a QColor. + + If the \a position is not valid, an invalid QColor is returned. + + \warning This function is expensive when used for massive pixel + manipulations. Use constBits() or constScanLine() when many + pixels needs to be read. + + \sa setPixel(), valid(), constBits(), constScanLine(), {QImage#Pixel Manipulation}{Pixel + Manipulation} +*/ + +/*! + \overload + \since 5.6 + + Returns the color of the pixel at coordinates (\a x, \a y) as a QColor. +*/ +QColor QImage::pixelColor(int x, int y) const +{ + if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) { + qWarning("QImage::pixelColor: coordinate (%d,%d) out of range", x, y); + return QColor(); + } + + const uchar * s = constScanLine(y); + switch (d->format) { + case Format_BGR30: + case Format_A2BGR30_Premultiplied: + return QColor(qConvertA2rgb30ToRgb64<PixelOrderBGR>(reinterpret_cast<const quint32 *>(s)[x])); + case Format_RGB30: + case Format_A2RGB30_Premultiplied: + return QColor(qConvertA2rgb30ToRgb64<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x])); + default: + return QColor(pixel(x, y)); + } +} + +/*! + \fn void QImage::setPixelColor(const QPoint &position, const QColor &color) + \since 5.6 + + Sets the color at the given \a position to \a color. + + If \a position is not a valid coordinate pair in the image, or + the image's format is either monochrome or paletted, the result is undefined. + + \warning This function is expensive due to the call of the internal + \c{detach()} function called within; if performance is a concern, we + recommend the use of scanLine() or bits() to access pixel data directly. + + \sa pixel(), bits(), scanLine(), {QImage#Pixel Manipulation}{Pixel Manipulation} +*/ + +/*! + \overload + \since 5.6 + + Sets the pixel color at (\a x, \a y) to \a color. +*/ +void QImage::setPixelColor(int x, int y, const QColor &color) +{ + if (!d || x < 0 || x >= width() || y < 0 || y >= height() || !color.isValid()) { + qWarning("QImage::setPixelColor: coordinate (%d,%d) out of range", x, y); + return; + } + // detach is called from within scanLine + uchar * s = scanLine(y); + switch (d->format) { + case Format_Mono: + case Format_MonoLSB: + case Format_Indexed8: + qWarning("QImage::setPixelColor: called on monochrome or indexed format"); + return; + case Format_BGR30: + ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderBGR>(color.rgba64()) | 0xc0000000; + return; + case Format_A2BGR30_Premultiplied: + ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderBGR>(color.rgba64()); + return; + case Format_RGB30: + ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(color.rgba64()) | 0xc0000000; + return; + case Format_A2RGB30_Premultiplied: + ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(color.rgba64()); + return; + default: + setPixel(x, y, color.rgba()); + return; + } +} + +/*! Returns \c true if all the colors in the image are shades of gray (i.e. their red, green and blue components are equal); otherwise false. diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 4ce99b9ab1..7751bbe3d3 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -34,10 +34,11 @@ #ifndef QIMAGE_H #define QIMAGE_H -#include <QtGui/qtransform.h> -#include <QtGui/qpaintdevice.h> +#include <QtGui/qcolor.h> #include <QtGui/qrgb.h> +#include <QtGui/qpaintdevice.h> #include <QtGui/qpixelformat.h> +#include <QtGui/qtransform.h> #include <QtCore/qbytearray.h> #include <QtCore/qrect.h> #include <QtCore/qstring.h> @@ -219,6 +220,12 @@ public: void setPixel(int x, int y, uint index_or_rgb); void setPixel(const QPoint &pt, uint index_or_rgb); + QColor pixelColor(int x, int y) const; + QColor pixelColor(const QPoint &pt) const; + + void setPixelColor(int x, int y, const QColor &c); + void setPixelColor(const QPoint &pt, const QColor &c); + QVector<QRgb> colorTable() const; #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) void setColorTable(const QVector<QRgb> &colors); @@ -350,6 +357,8 @@ inline bool QImage::valid(const QPoint &pt) const { return valid(pt.x(), pt.y()) inline int QImage::pixelIndex(const QPoint &pt) const { return pixelIndex(pt.x(), pt.y());} inline QRgb QImage::pixel(const QPoint &pt) const { return pixel(pt.x(), pt.y()); } inline void QImage::setPixel(const QPoint &pt, uint index_or_rgb) { setPixel(pt.x(), pt.y(), index_or_rgb); } +inline QColor QImage::pixelColor(const QPoint &pt) const { return pixelColor(pt.x(), pt.y()); } +inline void QImage::setPixelColor(const QPoint &pt, const QColor &c) { setPixelColor(pt.x(), pt.y(), c); } #if QT_DEPRECATED_SINCE(5, 0) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index b6f06135cd..b2992a138e 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -4698,10 +4698,8 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in } ++spans; } - if (!alphaSpans) { + if (!alphaSpans) op.destFetch = 0; - op.destFetch64 = 0; - } } } } @@ -4795,10 +4793,9 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) int length = spans->len; while (length) { int l = qMin(buffer_size, length); - QRgba64 *dest = op.destFetch64 ? op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.funcSolid64(dest, l, color, spans->coverage); - if (op.destStore64) - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); length -= l; x += l; } @@ -4987,7 +4984,7 @@ public: bool isSupported() const { - return op.srcFetch64 && op.func64; + return op.srcFetch64 && op.func64 && op.destFetch64 && op.destStore64; } const QRgba64 *fetch(int x, int y, int len) @@ -5108,10 +5105,9 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi while (length) { int l = qMin(buffer_size, length); const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l); - QRgba64 *dest = op.destFetch64 ? op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); - if (op.destStore64) - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); x += l; sx += l; length -= l; @@ -5353,10 +5349,9 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD if (buffer_size < l) l = buffer_size; const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l); - QRgba64 *dest = op.destFetch64 ? op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); - if (op.destStore64) - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); x += l; sx += l; length -= l; diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 10b79fdd0f..3c5fcba06e 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -203,6 +203,9 @@ private slots: void gradientPixelFormat_data(); void gradientPixelFormat(); + void gradientRgb30_data(); + void gradientRgb30(); + void fpe_pixmapTransform(); void fpe_zeroLengthLines(); void fpe_divByZero(); @@ -2451,36 +2454,50 @@ void tst_QPainter::setOpacity_data() QTest::newRow("RGBx8888 on RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_RGBX8888; - QTest::newRow("RGBA8888P on ARGB32P") << QImage::Format_RGBA8888_Premultiplied + QTest::newRow("RGBA8888P on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_RGBA8888_Premultiplied; + + QTest::newRow("RGBx8888 on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_RGBX8888; + + QTest::newRow("ARGB32P on RGBA8888P") << QImage::Format_RGBA8888_Premultiplied << QImage::Format_ARGB32_Premultiplied; - QTest::newRow("RGBx8888 on ARGB32P") << QImage::Format_RGBX8888 - << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("RGB32 on RGBx8888") << QImage::Format_RGBX8888 + << QImage::Format_RGB32; - QTest::newRow("ARGB32P on RGBA8888P") << QImage::Format_ARGB32_Premultiplied - << QImage::Format_RGBA8888_Premultiplied; + QTest::newRow("RGB30 on RGB32") << QImage::Format_RGB32 + << QImage::Format_BGR30; + + QTest::newRow("BGR30 on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_BGR30; - QTest::newRow("RGB32 on RGBx8888") << QImage::Format_RGB32 - << QImage::Format_RGBX8888; + QTest::newRow("A2RGB30P on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_A2BGR30_Premultiplied; QTest::newRow("A2RGB30P on A2RGB30P") << QImage::Format_A2RGB30_Premultiplied << QImage::Format_A2RGB30_Premultiplied; - QTest::newRow("ARGB32P on A2RGB30P") << QImage::Format_ARGB32_Premultiplied - << QImage::Format_A2RGB30_Premultiplied; + QTest::newRow("ARGB32P on A2RGB30P") << QImage::Format_A2RGB30_Premultiplied + << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("RGB32 on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied + << QImage::Format_RGB32; - QTest::newRow("RGB32 on A2BGR30P") << QImage::Format_ARGB32_Premultiplied - << QImage::Format_A2BGR30_Premultiplied; + QTest::newRow("RGB30 on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied + << QImage::Format_RGB30; - QTest::newRow("A2RGB30P on A2BGR30P") << QImage::Format_A2RGB30_Premultiplied - << QImage::Format_A2BGR30_Premultiplied; + QTest::newRow("A2RGB30P on A2BGR30P") << QImage::Format_A2BGR30_Premultiplied + << QImage::Format_A2RGB30_Premultiplied; - QTest::newRow("ARGB32P on BGR30") << QImage::Format_ARGB32_Premultiplied - << QImage::Format_BGR30; + QTest::newRow("ARGB32P on BGR30") << QImage::Format_BGR30 + << QImage::Format_ARGB32_Premultiplied; - QTest::newRow("ARGB32P on RGB30") << QImage::Format_A2RGB30_Premultiplied - << QImage::Format_RGB30; + QTest::newRow("ARGB32P on RGB30") << QImage::Format_RGB30 + << QImage::Format_ARGB32_Premultiplied; + + QTest::newRow("A2RGB30P on RGB30") << QImage::Format_RGB30 + << QImage::Format_A2RGB30_Premultiplied; } @@ -2562,7 +2579,7 @@ void tst_QPainter::drawhelper_blend_untransformed() QImage expected(size - 2, size, destFormat); p.begin(&expected); p.fillRect(0, 0, expected.width(), expected.height(), - QColor(dest.pixel(1, 0))); + dest.pixelColor(1, 0)); p.end(); const QImage subDest(dest.bits() + dest.depth() / 8, @@ -2570,9 +2587,7 @@ void tst_QPainter::drawhelper_blend_untransformed() dest.bytesPerLine(), dest.format()); if (dest.format() == QImage::Format_ARGB8565_Premultiplied || - dest.format() == QImage::Format_ARGB8555_Premultiplied || - dest.format() == QImage::Format_A2BGR30_Premultiplied || - dest.format() == QImage::Format_A2RGB30_Premultiplied ) { + dest.format() == QImage::Format_ARGB8555_Premultiplied) { // Test skipped due to rounding errors... continue; } @@ -2621,7 +2636,7 @@ void tst_QPainter::drawhelper_blend_tiled_untransformed() QImage expected(size - 2, size, destFormat); p.begin(&expected); p.fillRect(0, 0, expected.width(), expected.height(), - QColor(dest.pixel(1, 0))); + dest.pixelColor(1, 0)); p.end(); const QImage subDest(dest.bits() + dest.depth() / 8, @@ -3937,6 +3952,38 @@ void tst_QPainter::gradientInterpolation() } } +void tst_QPainter::gradientRgb30_data() +{ + QTest::addColumn<QColor>("stop0"); + QTest::addColumn<QColor>("stop1"); + + QTest::newRow("white->black") << QColor(Qt::white) << QColor(Qt::black); + QTest::newRow("blue->black") << QColor(Qt::blue) << QColor(Qt::black); + QTest::newRow("white->red") << QColor(Qt::white) << QColor(Qt::red); +} + +void tst_QPainter::gradientRgb30() +{ + QFETCH(QColor, stop0); + QFETCH(QColor, stop1); + + QLinearGradient gradient(0, 0, 512, 1); + gradient.setColorAt(0.0, stop0); + gradient.setColorAt(1.0, stop1); + + QImage image(512, 1, QImage::Format_RGB30); + QPainter painter(&image); + painter.fillRect(image.rect(), gradient); + painter.end(); + + for (int i = 0; i < 511; ++i) { + QColor p1 = image.pixelColor(i, 0); + QColor p2 = image.pixelColor(i + 1, 0); + QVERIFY(p1 != p2); + QVERIFY(qGray(p1.rgb()) >= qGray(p2.rgb())); + } +} + void tst_QPainter::drawPolygon() { QImage img(128, 128, QImage::Format_ARGB32_Premultiplied); @@ -4800,6 +4847,18 @@ void tst_QPainter::blendARGBonRGB_data() << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 125; QTest::newRow("ARGB_PM source-in RGB666") << QImage::Format_RGB666 << QImage::Format_ARGB32_Premultiplied << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 125; + QTest::newRow("ARGB over RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32 + << QPainter::CompositionMode_SourceOver << qRgba(255, 0, 0, 127) << 127; + QTest::newRow("ARGB_PM over RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_SourceOver << qRgba(127, 0, 0, 127) << 127; + QTest::newRow("ARGB source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32 + << QPainter::CompositionMode_Source << qRgba(255, 0, 0, 127) << 127; + QTest::newRow("ARGB_PM source RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_Source << qRgba(127, 0, 0, 127) << 127; + QTest::newRow("ARGB source-in RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32 + << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 127; + QTest::newRow("ARGB_PM source-in RGB30") << QImage::Format_RGB30 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 127; } void tst_QPainter::blendARGBonRGB() |