diff options
Diffstat (limited to 'src/gui')
78 files changed, 6254 insertions, 2982 deletions
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp index a9a880e71f..fe66c6e19d 100644 --- a/src/gui/accessible/qaccessiblecache.cpp +++ b/src/gui/accessible/qaccessiblecache.cpp @@ -90,7 +90,7 @@ QAccessible::Id QAccessibleCache::insert(QObject *object, QAccessibleInterface * Q_ASSERT(object == obj); if (obj) { objectToId.insert(obj, id); - connect(obj, SIGNAL(destroyed(QObject *)), this, SLOT(objectDestroyed(QObject *))); + connect(obj, &QObject::destroyed, this, &QAccessibleCache::objectDestroyed); } idToInterface.insert(id, iface); return id; diff --git a/src/gui/doc/snippets/image/supportedformat.cpp b/src/gui/doc/snippets/image/supportedformat.cpp index f4d2606728..0b52156b80 100644 --- a/src/gui/doc/snippets/image/supportedformat.cpp +++ b/src/gui/doc/snippets/image/supportedformat.cpp @@ -43,10 +43,10 @@ int main(int argv, char **args) { //! [0] - QImageWriter writer; - writer.setFormat("png"); - if (writer.supportsOption(QImageIOHandler::Description)) - qDebug() << "Png supports embedded text"; + QImageWriter writer; + writer.setFormat("png"); + if (writer.supportsOption(QImageIOHandler::Description)) + qDebug() << "Png supports embedded text"; //! [0] return 0; } diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc index e66afcfe55..d9c7c7e3c1 100644 --- a/src/gui/doc/src/coordsys.qdoc +++ b/src/gui/doc/src/coordsys.qdoc @@ -29,7 +29,7 @@ \page coordsys.html \title Coordinate System \ingroup qt-graphics - \ingroup best-practices + \ingroup best-practices \brief Information about the coordinate system used by the paint system. diff --git a/src/gui/doc/src/paintsystem.qdoc b/src/gui/doc/src/paintsystem.qdoc index cd208e9e18..1006d2ef65 100644 --- a/src/gui/doc/src/paintsystem.qdoc +++ b/src/gui/doc/src/paintsystem.qdoc @@ -93,8 +93,8 @@ \page paintsystem-devices.html \title Paint Devices and Backends - \contentspage The Paint System - \nextpage Drawing and Filling + \contentspage The Paint System + \nextpage Drawing and Filling \section1 Creating a Paint Device diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index bf4b5ddf01..bbdd0f3da7 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -32,6 +32,7 @@ HEADERS += \ SOURCES += \ image/qbitmap.cpp \ image/qimage.cpp \ + image/qimage_conversions.cpp \ image/qimageiohandler.cpp \ image/qimagereader.cpp \ image/qimagewriter.cpp \ @@ -54,6 +55,9 @@ SOURCES += \ win32:!winrt: SOURCES += image/qpixmap_win.cpp +NO_PCH_SOURCES += image/qimage_compat.cpp +false: SOURCES += $$NO_PCH_SOURCES # Hack for QtCreator + # Built-in image format support HEADERS += \ image/qbmphandler_p.h \ @@ -74,9 +78,12 @@ contains(QT_CONFIG, jpeg):include($$PWD/qjpeghandler.pri) contains(QT_CONFIG, gif):include($$PWD/qgifhandler.pri) # SIMD -NEON_SOURCES += image/qimage_neon.cpp -SSE2_SOURCES += image/qimage_sse2.cpp -SSSE3_SOURCES += image/qimage_ssse3.cpp -AVX_SOURCES += image/qimage_avx.cpp +contains(QT_CPU_FEATURES.$$QT_ARCH, neon) { + SOURCES += image/qimage_neon.cpp +} +contains(QT_CPU_FEATURES.$$QT_ARCH, sse2) { + SOURCES += image/qimage_sse2.cpp + SSSE3_SOURCES += image/qimage_ssse3.cpp +} MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index c03d9b8e5d..bb79a139b3 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -210,54 +210,15 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int uint red_mask = 0; uint green_mask = 0; uint blue_mask = 0; + uint alpha_mask = 0; int red_shift = 0; int green_shift = 0; int blue_shift = 0; + int alpha_shift = 0; int red_scale = 0; int green_scale = 0; int blue_scale = 0; - - int ncols = 0; - int depth = 0; - QImage::Format format; - switch (nbits) { - case 32: - case 24: - case 16: - depth = 32; - format = QImage::Format_RGB32; - break; - case 8: - case 4: - depth = 8; - format = QImage::Format_Indexed8; - break; - default: - depth = 1; - format = QImage::Format_Mono; - } - - if (bi.biHeight < 0) - h = -h; // support images with negative height - - if (image.size() != QSize(w, h) || image.format() != format) { - image = QImage(w, h, format); - if (image.isNull()) // could not create image - return false; - } - - if (depth != 32) { - ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits; - if (ncols > 256) // sanity check - don't run out of mem if color table is broken - return false; - image.setColorCount(ncols); - } - - image.setDotsPerMeterX(bi.biXPelsPerMeter); - image.setDotsPerMeterY(bi.biYPelsPerMeter); - - if (!d->isSequential()) - d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4? BMP_WIN : bi.biSize)); // goto start of colormap + int alpha_scale = 0; if (bi.biSize >= BMP_WIN4 || (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32))) { if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask)) @@ -269,7 +230,6 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int // Read BMP v4+ header if (bi.biSize >= BMP_WIN4) { - int alpha_mask = 0; int CSType = 0; int gamma_red = 0; int gamma_green = 0; @@ -307,6 +267,49 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int } } + bool transp = (comp == BMP_BITFIELDS) && alpha_mask; + int ncols = 0; + int depth = 0; + QImage::Format format; + switch (nbits) { + case 32: + case 24: + case 16: + depth = 32; + format = transp ? QImage::Format_ARGB32 : QImage::Format_RGB32; + break; + case 8: + case 4: + depth = 8; + format = QImage::Format_Indexed8; + break; + default: + depth = 1; + format = QImage::Format_Mono; + } + + if (bi.biHeight < 0) + h = -h; // support images with negative height + + if (image.size() != QSize(w, h) || image.format() != format) { + image = QImage(w, h, format); + if (image.isNull()) // could not create image + return false; + } + + if (depth != 32) { + ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits; + if (ncols > 256) // sanity check - don't run out of mem if color table is broken + return false; + image.setColorCount(ncols); + } + + image.setDotsPerMeterX(bi.biXPelsPerMeter); + image.setDotsPerMeterY(bi.biYPelsPerMeter); + + if (!d->isSequential()) + d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4? BMP_WIN : bi.biSize)); // goto start of colormap + if (ncols > 0) { // read color table uchar rgb[4]; int rgb_len = t == BMP_OLD ? 3 : 4; @@ -324,6 +327,8 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int green_scale = 256 / ((green_mask >> green_shift) + 1); blue_shift = calc_shift(blue_mask); blue_scale = 256 / ((blue_mask >> blue_shift) + 1); + alpha_shift = calc_shift(alpha_mask); + alpha_scale = 256 / ((alpha_mask >> alpha_shift) + 1); } else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) { blue_mask = 0x000000ff; green_mask = 0x0000ff00; @@ -344,6 +349,13 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int blue_scale = 8; } +#if 0 + qDebug("Rmask: %08x Rshift: %08x Rscale:%08x", red_mask, red_shift, red_scale); + qDebug("Gmask: %08x Gshift: %08x Gscale:%08x", green_mask, green_shift, green_scale); + qDebug("Bmask: %08x Bshift: %08x Bscale:%08x", blue_mask, blue_shift, blue_scale); + qDebug("Amask: %08x Ashift: %08x Ascale:%08x", alpha_mask, alpha_shift, alpha_scale); +#endif + // offset can be bogus, be careful if (offset>=0 && startpos + offset > d->pos()) { if (!d->isSequential()) @@ -535,11 +547,14 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int b = buf24; while (p < end) { c = *(uchar*)b | (*(uchar*)(b+1)<<8); - if (nbits != 16) + if (nbits > 16) c |= *(uchar*)(b+2)<<16; - *p++ = qRgb(((c & red_mask) >> red_shift) * red_scale, + if (nbits > 24) + c |= *(uchar*)(b+3)<<24; + *p++ = qRgba(((c & red_mask) >> red_shift) * red_scale, ((c & green_mask) >> green_shift) * green_scale, - ((c & blue_mask) >> blue_shift) * blue_scale); + ((c & blue_mask) >> blue_shift) * blue_scale, + transp ? ((c & alpha_mask) >> alpha_shift) * alpha_scale : 0xff); b += nbits/8; } } diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 70fe7b783f..d6037fb2d6 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -715,42 +715,6 @@ bool QImageData::checkForAlphaPixels() const QImage member functions *****************************************************************************/ -// table to flip bits -static const uchar bitflip[256] = { - /* - open OUT, "| fmt"; - for $i (0..255) { - print OUT (($i >> 7) & 0x01) | (($i >> 5) & 0x02) | - (($i >> 3) & 0x04) | (($i >> 1) & 0x08) | - (($i << 7) & 0x80) | (($i << 5) & 0x40) | - (($i << 3) & 0x20) | (($i << 1) & 0x10), ", "; - } - close OUT; - */ - 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, - 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, - 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, - 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, - 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, - 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, - 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, - 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, - 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, - 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, - 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, - 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, - 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, - 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, - 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, - 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 -}; - -const uchar *qt_get_bitflip_array() // called from QPixmap code -{ - return bitflip; -} - - /*! Constructs a null image. @@ -1105,8 +1069,7 @@ void QImage::detach() if (d->ref.load() != 1 || d->ro_data) *this = copy(); - if (d) - ++d->detach_no; + ++d->detach_no; } } @@ -1898,2100 +1861,6 @@ QImage::Format QImage::format() const return d ? d->format : Format_Invalid; } - - -/***************************************************************************** - Internal routines for converting image depth. - *****************************************************************************/ - -typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - -typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags); - -static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888); - Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = PREMUL(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static bool convert_ARGB_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_ARGB32); - - const int pad = (data->bytes_per_line >> 2) - data->width; - QRgb *rgb_data = (QRgb *) data->data; - - for (int i = 0; i < data->height; ++i) { - const QRgb *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = PREMUL(*rgb_data); - ++rgb_data; - } - rgb_data += pad; - } - data->format = QImage::Format_ARGB32_Premultiplied; - return true; -} - -static void convert_ARGB_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32); - Q_ASSERT(dest->format == QImage::Format_RGBX8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(0xff000000 | *src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_ARGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static bool convert_ARGB_to_RGBA_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_ARGB32_Premultiplied); - - const int pad = (data->bytes_per_line >> 2) - data->width; - quint32 *rgb_data = (quint32 *) data->data; - - for (int i = 0; i < data->height; ++i) { - const quint32 *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = ARGB2RGBA(*rgb_data); - ++rgb_data; - } - rgb_data += pad; - } - if (data->format == QImage::Format_ARGB32) - data->format = QImage::Format_RGBA8888; - else - data->format = QImage::Format_RGBA8888_Premultiplied; - return true; -} - -static void convert_ARGB_to_RGBA_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32); - Q_ASSERT(dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(PREMUL(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_RGBA_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBX8888 || src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = RGBA2ARGB(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888 || data->format == QImage::Format_RGBA8888_Premultiplied); - - const int pad = (data->bytes_per_line >> 2) - data->width; - QRgb *rgb_data = (QRgb *) data->data; - - for (int i = 0; i < data->height; ++i) { - const QRgb *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = RGBA2ARGB(*rgb_data); - ++rgb_data; - } - rgb_data += pad; - } - if (data->format == QImage::Format_RGBA8888_Premultiplied) - data->format = QImage::Format_ARGB32_Premultiplied; - else if (data->format == QImage::Format_RGBX8888) - data->format = QImage::Format_RGB32; - else - data->format = QImage::Format_ARGB32; - return true; -} - -static void convert_RGBA_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA8888); - Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = PREMUL(RGBA2ARGB(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static bool convert_RGBA_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGBA8888); - - const int pad = (data->bytes_per_line >> 2) - data->width; - QRgb *rgb_data = (QRgb *) data->data; - - for (int i = 0; i < data->height; ++i) { - const QRgb *end = rgb_data + data->width; - while (rgb_data < end) { - *rgb_data = PREMUL(RGBA2ARGB(*rgb_data)); - ++rgb_data; - } - rgb_data += pad; - } - data->format = QImage::Format_ARGB32_Premultiplied; - return true; -} - -static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - const int depth = 32; - - const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const int nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; // end of src - quint32 *dest_data = (quint32 *) (newData + nbytes); // end of dest > end of src - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - for (int i = 0; i < data->colortable.size(); ++i) - data->colortable[i] = PREMUL(data->colortable.at(i)); - - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_ARGB32_Premultiplied; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = nbytes; - - return true; -} - -static bool convert_indexed8_to_RGB_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - const int depth = 32; - - const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const int nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint32 *dest_data = (quint32 *) (newData + nbytes); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = (quint32) data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_RGB32; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = nbytes; - - return true; -} - -static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - const int depth = 16; - - const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const int nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, nbytes); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint16 *dest_data = (quint16 *) (newData + nbytes); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (dst_bytes_per_line >> 1) - width; - - quint16 colorTableRGB16[256]; - if (data->colortable.isEmpty()) { - for (int i = 0; i < 256; ++i) - colorTableRGB16[i] = qConvertRgb32To16(qRgb(i, i, i)); - } else { - // 1) convert the existing colors to RGB16 - const int tableSize = data->colortable.size(); - for (int i = 0; i < tableSize; ++i) - colorTableRGB16[i] = qConvertRgb32To16(data->colortable.at(i)); - data->colortable = QVector<QRgb>(); - - // 2) fill the rest of the table in case src_data > colortable.size() - const quint16 lastColor = colorTableRGB16[tableSize - 1]; - for (int i = tableSize; i < 256; ++i) - colorTableRGB16[i] = lastColor; - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = colorTableRGB16[*src_data]; - } - } - - data->format = QImage::Format_RGB16; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = nbytes; - - return true; -} - -static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGB32); - const int depth = 16; - - const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const int src_bytes_per_line = data->bytes_per_line; - quint32 *src_data = (quint32 *) data->data; - quint16 *dst_data = (quint16 *) data->data; - - for (int i = 0; i < data->height; ++i) { - for (int j = 0; j < data->width; ++j) - dst_data[j] = qConvertRgb32To16(src_data[j]); - src_data = (quint32 *) (((char*)src_data) + src_bytes_per_line); - dst_data = (quint16 *) (((char*)dst_data) + dst_bytes_per_line); - } - data->format = QImage::Format_RGB16; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, data->nbytes); - if (newData) { - data->data = newData; - return true; - } else { - return false; - } -} - -static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_RGBA8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = INV_PREMUL(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_ARGB_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_RGBX8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = 0xff000000 | INV_PREMUL(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_ARGB_PM_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGBX8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(0xff000000 | INV_PREMUL(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_ARGB_PM_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGBA8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(INV_PREMUL(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_RGBA_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBX8888); - Q_ASSERT(dest->format == QImage::Format_RGB32); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const uint *src_data = (const uint *)src->data; - uint *dest_data = (uint *)dest->data; - - for (int i = 0; i < src->height; ++i) { - const uint *end = src_data + src->width; - while (src_data < end) { - *dest_data = RGBA2ARGB(*src_data) | 0xff000000; - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_RGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGB32); - Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const uint *src_data = (const uint *)src->data; - uint *dest_data = (uint *)dest->data; - - for (int i = 0; i < src->height; ++i) { - const uint *end = src_data + src->width; - while (src_data < end) { - *dest_data = ARGB2RGBA(*src_data | 0xff000000); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_RGBA_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_ARGB32); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = INV_PREMUL(RGBA2ARGB(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void convert_RGBA_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_RGB32); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = 0xff000000 | INV_PREMUL(RGBA2ARGB(*src_data)); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void swap_bit_order(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); - Q_ASSERT(dest->format == QImage::Format_Mono || dest->format == QImage::Format_MonoLSB); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - Q_ASSERT(src->nbytes == dest->nbytes); - Q_ASSERT(src->bytes_per_line == dest->bytes_per_line); - - dest->colortable = src->colortable; - - const uchar *src_data = src->data; - const uchar *end = src->data + src->nbytes; - uchar *dest_data = dest->data; - while (src_data < end) { - *dest_data = bitflip[*src_data]; - ++src_data; - ++dest_data; - } -} - -static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const uint *src_data = (const uint *)src->data; - uint *dest_data = (uint *)dest->data; - - for (int i = 0; i < src->height; ++i) { - const uint *end = src_data + src->width; - while (src_data < end) { - *dest_data = *src_data | 0xff000000; - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - -static void mask_alpha_converter_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags) -{ -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return mask_alpha_converter(dest, src, flags); -#else - Q_UNUSED(flags); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const uint *src_data = (const uint *)src->data; - uint *dest_data = (uint *)dest->data; - - for (int i = 0; i < src->height; ++i) { - const uint *end = src_data + src->width; - while (src_data < end) { - *dest_data = *src_data | 0x000000ff; - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -#endif -} - -static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) -{ - QVector<QRgb> colorTable = ctbl; - if (format == QImage::Format_RGB32) { - // check if the color table has alpha - for (int i = 0; i < colorTable.size(); ++i) - if (qAlpha(colorTable.at(i) != 0xff)) - colorTable[i] = colorTable.at(i) | 0xff000000; - } else if (format == QImage::Format_ARGB32_Premultiplied) { - // check if the color table has alpha - for (int i = 0; i < colorTable.size(); ++i) - colorTable[i] = PREMUL(colorTable.at(i)); - } - return colorTable; -} - -// -// dither_to_1: Uses selected dithering algorithm. -// - -static void dither_to_Mono(QImageData *dst, const QImageData *src, - Qt::ImageConversionFlags flags, bool fromalpha) -{ - Q_ASSERT(src->width == dst->width); - Q_ASSERT(src->height == dst->height); - Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB); - - dst->colortable.clear(); - dst->colortable.append(0xffffffff); - dst->colortable.append(0xff000000); - - enum { Threshold, Ordered, Diffuse } dithermode; - - if (fromalpha) { - if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither) - dithermode = Diffuse; - else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither) - dithermode = Ordered; - else - dithermode = Threshold; - } else { - if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) - dithermode = Threshold; - else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither) - dithermode = Ordered; - else - dithermode = Diffuse; - } - - int w = src->width; - int h = src->height; - int d = src->depth; - uchar gray[256]; // gray map for 8 bit images - bool use_gray = (d == 8); - if (use_gray) { // make gray map - if (fromalpha) { - // Alpha 0x00 -> 0 pixels (white) - // Alpha 0xFF -> 1 pixels (black) - for (int i = 0; i < src->colortable.size(); i++) - gray[i] = (255 - (src->colortable.at(i) >> 24)); - } else { - // Pixel 0x00 -> 1 pixels (black) - // Pixel 0xFF -> 0 pixels (white) - for (int i = 0; i < src->colortable.size(); i++) - gray[i] = qGray(src->colortable.at(i)); - } - } - - uchar *dst_data = dst->data; - int dst_bpl = dst->bytes_per_line; - const uchar *src_data = src->data; - int src_bpl = src->bytes_per_line; - - switch (dithermode) { - case Diffuse: { - QScopedArrayPointer<int> lineBuffer(new int[w * 2]); - int *line1 = lineBuffer.data(); - int *line2 = lineBuffer.data() + w; - int bmwidth = (w+7)/8; - - int *b1, *b2; - int wbytes = w * (d/8); - const uchar *p = src->data; - const uchar *end = p + wbytes; - b2 = line2; - if (use_gray) { // 8 bit image - while (p < end) - *b2++ = gray[*p++]; - } else { // 32 bit image - if (fromalpha) { - while (p < end) { - *b2++ = 255 - (*(uint*)p >> 24); - p += 4; - } - } else { - while (p < end) { - *b2++ = qGray(*(uint*)p); - p += 4; - } - } - } - for (int y=0; y<h; y++) { // for each scan line... - int *tmp = line1; line1 = line2; line2 = tmp; - bool not_last_line = y < h - 1; - if (not_last_line) { // calc. grayvals for next line - p = src->data + (y+1)*src->bytes_per_line; - end = p + wbytes; - b2 = line2; - if (use_gray) { // 8 bit image - while (p < end) - *b2++ = gray[*p++]; - } else { // 24 bit image - if (fromalpha) { - while (p < end) { - *b2++ = 255 - (*(uint*)p >> 24); - p += 4; - } - } else { - while (p < end) { - *b2++ = qGray(*(uint*)p); - p += 4; - } - } - } - } - - int err; - uchar *p = dst->data + y*dst->bytes_per_line; - memset(p, 0, bmwidth); - b1 = line1; - b2 = line2; - int bit = 7; - for (int x=1; x<=w; x++) { - if (*b1 < 128) { // black pixel - err = *b1++; - *p |= 1 << bit; - } else { // white pixel - err = *b1++ - 255; - } - if (bit == 0) { - p++; - bit = 7; - } else { - bit--; - } - if (x < w) - *b1 += (err*7)>>4; // spread error to right pixel - if (not_last_line) { - b2[0] += (err*5)>>4; // pixel below - if (x > 1) - b2[-1] += (err*3)>>4; // pixel below left - if (x < w) - b2[1] += err>>4; // pixel below right - } - b2++; - } - } - } break; - case Ordered: { - - memset(dst->data, 0, dst->nbytes); - if (d == 32) { - for (int i=0; i<h; i++) { - const uint *p = (const uint *)src_data; - const uint *end = p + w; - uchar *m = dst_data; - int bit = 7; - int j = 0; - if (fromalpha) { - while (p < end) { - if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15]) - *m |= 1 << bit; - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - } else { - while (p < end) { - if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15]) - *m |= 1 << bit; - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - } - dst_data += dst_bpl; - src_data += src_bpl; - } - } else - /* (d == 8) */ { - for (int i=0; i<h; i++) { - const uchar *p = src_data; - const uchar *end = p + w; - uchar *m = dst_data; - int bit = 7; - int j = 0; - while (p < end) { - if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15]) - *m |= 1 << bit; - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - dst_data += dst_bpl; - src_data += src_bpl; - } - } - } break; - default: { // Threshold: - memset(dst->data, 0, dst->nbytes); - if (d == 32) { - for (int i=0; i<h; i++) { - const uint *p = (const uint *)src_data; - const uint *end = p + w; - uchar *m = dst_data; - int bit = 7; - if (fromalpha) { - while (p < end) { - if ((*p++ >> 24) >= 128) - *m |= 1 << bit; // Set mask "on" - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - } else { - while (p < end) { - if (qGray(*p++) < 128) - *m |= 1 << bit; // Set pixel "black" - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - } - dst_data += dst_bpl; - src_data += src_bpl; - } - } else - if (d == 8) { - for (int i=0; i<h; i++) { - const uchar *p = src_data; - const uchar *end = p + w; - uchar *m = dst_data; - int bit = 7; - while (p < end) { - if (gray[*p++] < 128) - *m |= 1 << bit; // Set mask "on"/ pixel "black" - if (bit == 0) { - m++; - bit = 7; - } else { - bit--; - } - } - dst_data += dst_bpl; - src_data += src_bpl; - } - } - } - } - - if (dst->format == QImage::Format_MonoLSB) { - // need to swap bit order - uchar *sl = dst->data; - int bpl = (dst->width + 7) * dst->depth / 8; - int pad = dst->bytes_per_line - bpl; - for (int y=0; y<dst->height; ++y) { - for (int x=0; x<bpl; ++x) { - *sl = bitflip[*sl]; - ++sl; - } - sl += pad; - } - } -} - -static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) -{ - dither_to_Mono(dst, src, flags, false); -} - -static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) -{ - QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); - convert_ARGB_PM_to_ARGB(tmp.data(), src, flags); - dither_to_Mono(dst, tmp.data(), flags, false); -} - -// -// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit -// image with a colormap. If the 32 bit image has more than 256 colors, -// we convert the red,green and blue bytes into a single byte encoded -// as 6 shades of each of red, green and blue. -// -// if dithering is needed, only 1 color at most is available for alpha. -// -struct QRgbMap { - inline QRgbMap() : used(0) { } - uchar pix; - uchar used; - QRgb rgb; -}; - -static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) -{ - Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32); - Q_ASSERT(dst->format == QImage::Format_Indexed8); - Q_ASSERT(src->width == dst->width); - Q_ASSERT(src->height == dst->height); - - bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither - || src->format == QImage::Format_ARGB32; - uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0; - - const int tablesize = 997; // prime - QRgbMap table[tablesize]; - int pix=0; - - if (!dst->colortable.isEmpty()) { - QVector<QRgb> ctbl = dst->colortable; - dst->colortable.resize(256); - // Preload palette into table. - // Almost same code as pixel insertion below - for (int i = 0; i < dst->colortable.size(); ++i) { - // Find in table... - QRgb p = ctbl.at(i) | alpha_mask; - int hash = p % tablesize; - for (;;) { - if (table[hash].used) { - if (table[hash].rgb == p) { - // Found previous insertion - use it - break; - } else { - // Keep searching... - if (++hash == tablesize) hash = 0; - } - } else { - // Cannot be in table - Q_ASSERT (pix != 256); // too many colors - // Insert into table at this unused position - dst->colortable[pix] = p; - table[hash].pix = pix++; - table[hash].rgb = p; - table[hash].used = 1; - break; - } - } - } - } - - if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) { - dst->colortable.resize(256); - const uchar *src_data = src->data; - uchar *dest_data = dst->data; - for (int y = 0; y < src->height; y++) { // check if <= 256 colors - const QRgb *s = (const QRgb *)src_data; - uchar *b = dest_data; - for (int x = 0; x < src->width; ++x) { - QRgb p = s[x] | alpha_mask; - int hash = p % tablesize; - for (;;) { - if (table[hash].used) { - if (table[hash].rgb == (p)) { - // Found previous insertion - use it - break; - } else { - // Keep searching... - if (++hash == tablesize) hash = 0; - } - } else { - // Cannot be in table - if (pix == 256) { // too many colors - do_quant = true; - // Break right out - x = src->width; - y = src->height; - } else { - // Insert into table at this unused position - dst->colortable[pix] = p; - table[hash].pix = pix++; - table[hash].rgb = p; - table[hash].used = 1; - } - break; - } - } - *b++ = table[hash].pix; // May occur once incorrectly - } - src_data += src->bytes_per_line; - dest_data += dst->bytes_per_line; - } - } - int numColors = do_quant ? 256 : pix; - - dst->colortable.resize(numColors); - - if (do_quant) { // quantization needed - -#define MAX_R 5 -#define MAX_G 5 -#define MAX_B 5 -#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b)) - - for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube - for (int gc=0; gc<=MAX_G; gc++) - for (int bc=0; bc<=MAX_B; bc++) - dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B); - - const uchar *src_data = src->data; - uchar *dest_data = dst->data; - if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) { - for (int y = 0; y < src->height; y++) { - const QRgb *p = (const QRgb *)src_data; - const QRgb *end = p + src->width; - uchar *b = dest_data; - - while (p < end) { -#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255)) - *b++ = - INDEXOF( - DITHER(qRed(*p), MAX_R), - DITHER(qGreen(*p), MAX_G), - DITHER(qBlue(*p), MAX_B) - ); -#undef DITHER - p++; - } - src_data += src->bytes_per_line; - dest_data += dst->bytes_per_line; - } - } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) { - int* line1[3]; - int* line2[3]; - int* pv[3]; - QScopedArrayPointer<int> lineBuffer(new int[src->width * 9]); - line1[0] = lineBuffer.data(); - line2[0] = lineBuffer.data() + src->width; - line1[1] = lineBuffer.data() + src->width * 2; - line2[1] = lineBuffer.data() + src->width * 3; - line1[2] = lineBuffer.data() + src->width * 4; - line2[2] = lineBuffer.data() + src->width * 5; - pv[0] = lineBuffer.data() + src->width * 6; - pv[1] = lineBuffer.data() + src->width * 7; - pv[2] = lineBuffer.data() + src->width * 8; - - int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian); - for (int y = 0; y < src->height; y++) { - const uchar* q = src_data; - const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data; - uchar *b = dest_data; - for (int chan = 0; chan < 3; chan++) { - int *l1 = (y&1) ? line2[chan] : line1[chan]; - int *l2 = (y&1) ? line1[chan] : line2[chan]; - if (y == 0) { - for (int i = 0; i < src->width; i++) - l1[i] = q[i*4+chan+endian]; - } - if (y+1 < src->height) { - for (int i = 0; i < src->width; i++) - l2[i] = q2[i*4+chan+endian]; - } - // Bi-directional error diffusion - if (y&1) { - for (int x = 0; x < src->width; x++) { - int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0); - int err = l1[x] - pix * 255 / 5; - pv[chan][x] = pix; - - // Spread the error around... - if (x + 1< src->width) { - l1[x+1] += (err*7)>>4; - l2[x+1] += err>>4; - } - l2[x]+=(err*5)>>4; - if (x>1) - l2[x-1]+=(err*3)>>4; - } - } else { - for (int x = src->width; x-- > 0;) { - int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0); - int err = l1[x] - pix * 255 / 5; - pv[chan][x] = pix; - - // Spread the error around... - if (x > 0) { - l1[x-1] += (err*7)>>4; - l2[x-1] += err>>4; - } - l2[x]+=(err*5)>>4; - if (x + 1 < src->width) - l2[x+1]+=(err*3)>>4; - } - } - } - if (endian) { - for (int x = 0; x < src->width; x++) { - *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]); - } - } else { - for (int x = 0; x < src->width; x++) { - *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]); - } - } - src_data += src->bytes_per_line; - dest_data += dst->bytes_per_line; - } - } else { // OrderedDither - for (int y = 0; y < src->height; y++) { - const QRgb *p = (const QRgb *)src_data; - const QRgb *end = p + src->width; - uchar *b = dest_data; - - int x = 0; - while (p < end) { - uint d = qt_bayer_matrix[y & 15][x & 15] << 8; - -#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16)) - *b++ = - INDEXOF( - DITHER(qRed(*p), d, MAX_R), - DITHER(qGreen(*p), d, MAX_G), - DITHER(qBlue(*p), d, MAX_B) - ); -#undef DITHER - - p++; - x++; - } - src_data += src->bytes_per_line; - dest_data += dst->bytes_per_line; - } - } - - if (src->format != QImage::Format_RGB32 - && src->format != QImage::Format_RGB16) { - const int trans = 216; - Q_ASSERT(dst->colortable.size() > trans); - dst->colortable[trans] = 0; - QScopedPointer<QImageData> mask(QImageData::create(QSize(src->width, src->height), QImage::Format_Mono)); - dither_to_Mono(mask.data(), src, flags, true); - uchar *dst_data = dst->data; - const uchar *mask_data = mask->data; - for (int y = 0; y < src->height; y++) { - for (int x = 0; x < src->width ; x++) { - if (!(mask_data[x>>3] & (0x80 >> (x & 7)))) - dst_data[x] = trans; - } - mask_data += mask->bytes_per_line; - dst_data += dst->bytes_per_line; - } - dst->has_alpha_clut = true; - } - -#undef MAX_R -#undef MAX_G -#undef MAX_B -#undef INDEXOF - - } -} - -static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) -{ - QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); - convert_ARGB_PM_to_ARGB(tmp.data(), src, flags); - convert_RGB_to_Indexed8(dst, tmp.data(), flags); -} - -static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) -{ - convert_RGB_to_Indexed8(dst, src, flags); -} - -static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_Indexed8); - Q_ASSERT(dest->format == QImage::Format_RGB32 - || dest->format == QImage::Format_ARGB32 - || dest->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format); - if (colorTable.size() == 0) { - colorTable.resize(256); - for (int i=0; i<256; ++i) - colorTable[i] = qRgb(i, i, i); - } - - int w = src->width; - const uchar *src_data = src->data; - uchar *dest_data = dest->data; - int tableSize = colorTable.size() - 1; - for (int y = 0; y < src->height; y++) { - uint *p = (uint *)dest_data; - const uchar *b = src_data; - uint *end = p + w; - - while (p < end) - *p++ = colorTable.at(qMin<int>(tableSize, *b++)); - - src_data += src->bytes_per_line; - dest_data += dest->bytes_per_line; - } -} - -static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); - Q_ASSERT(dest->format == QImage::Format_RGB32 - || dest->format == QImage::Format_ARGB32 - || dest->format == QImage::Format_ARGB32_Premultiplied); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format); - - // Default to black / white colors - if (colorTable.size() < 2) { - if (colorTable.size() == 0) - colorTable << 0xff000000; - colorTable << 0xffffffff; - } - - const uchar *src_data = src->data; - uchar *dest_data = dest->data; - if (src->format == QImage::Format_Mono) { - for (int y = 0; y < dest->height; y++) { - uint *p = (uint *)dest_data; - for (int x = 0; x < dest->width; x++) - *p++ = colorTable.at((src_data[x>>3] >> (7 - (x & 7))) & 1); - - src_data += src->bytes_per_line; - dest_data += dest->bytes_per_line; - } - } else { - for (int y = 0; y < dest->height; y++) { - uint *p = (uint *)dest_data; - for (int x = 0; x < dest->width; x++) - *p++ = colorTable.at((src_data[x>>3] >> (x & 7)) & 1); - - src_data += src->bytes_per_line; - dest_data += dest->bytes_per_line; - } - } -} - - -static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); - Q_ASSERT(dest->format == QImage::Format_Indexed8); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - QVector<QRgb> ctbl = src->colortable; - if (ctbl.size() > 2) { - ctbl.resize(2); - } else if (ctbl.size() < 2) { - if (ctbl.size() == 0) - ctbl << 0xff000000; - ctbl << 0xffffffff; - } - dest->colortable = ctbl; - dest->has_alpha_clut = src->has_alpha_clut; - - - const uchar *src_data = src->data; - uchar *dest_data = dest->data; - if (src->format == QImage::Format_Mono) { - for (int y = 0; y < dest->height; y++) { - uchar *p = dest_data; - for (int x = 0; x < dest->width; x++) - *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1; - src_data += src->bytes_per_line; - dest_data += dest->bytes_per_line; - } - } else { - for (int y = 0; y < dest->height; y++) { - uchar *p = dest_data; - for (int x = 0; x < dest->width; x++) - *p++ = (src_data[x>>3] >> (x & 7)) & 1; - src_data += src->bytes_per_line; - dest_data += dest->bytes_per_line; - } - } -} - -// Cannot be used with indexed formats. -static void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) -{ - const int buffer_size = 2048; - uint buffer[buffer_size]; - const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; - const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; - - FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp]; - StorePixelsFunc store = qStorePixels[destLayout->bpp]; - - for (int y = 0; y < src->height; ++y) { - int x = 0; - while (x < src->width) { - int l = qMin(src->width - x, buffer_size); - const uint *ptr = fetch(buffer, srcData, x, l); - ptr = srcLayout->convertToARGB32PM(buffer, ptr, l, srcLayout, 0); - ptr = destLayout->convertFromARGB32PM(buffer, ptr, l, destLayout, 0); - store(destData, ptr, x, l); - x += l; - } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } -} - - -// first index source, second dest -static Image_Converter converter_map[QImage::NImageFormats][QImage::NImageFormats] = -{ - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, - 0, - swap_bit_order, - convert_Mono_to_Indexed8, - convert_Mono_to_X32, - convert_Mono_to_X32, - convert_Mono_to_X32, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_Mono - - { - 0, - swap_bit_order, - 0, - convert_Mono_to_Indexed8, - convert_Mono_to_X32, - convert_Mono_to_X32, - convert_Mono_to_X32, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_MonoLSB - - { - 0, - convert_X_to_Mono, - convert_X_to_Mono, - 0, - convert_Indexed8_to_X32, - convert_Indexed8_to_X32, - convert_Indexed8_to_X32, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_Indexed8 - - { - 0, - convert_X_to_Mono, - convert_X_to_Mono, - convert_RGB_to_Indexed8, - 0, - mask_alpha_converter, - mask_alpha_converter, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_RGB_to_RGBA, - convert_RGB_to_RGBA, - convert_RGB_to_RGBA - }, // Format_RGB32 - - { - 0, - convert_X_to_Mono, - convert_X_to_Mono, - convert_ARGB_to_Indexed8, - mask_alpha_converter, - 0, - convert_ARGB_to_ARGB_PM, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_generic, - convert_ARGB_to_RGBx, - convert_ARGB_to_RGBA, - convert_ARGB_to_RGBA_PM, - }, // Format_ARGB32 - - { - 0, - convert_ARGB_PM_to_Mono, - convert_ARGB_PM_to_Mono, - convert_ARGB_PM_to_Indexed8, - convert_ARGB_PM_to_RGB, - convert_ARGB_PM_to_ARGB, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - convert_ARGB_PM_to_RGBx, - convert_ARGB_PM_to_RGBA, - convert_ARGB_to_RGBA, - }, // Format_ARGB32_Premultiplied - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, -#if defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16) - convert_generic, -#else - 0, -#endif - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_RGB16 - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_ARGB8565_Premultiplied - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_RGB666 - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_ARGB6666_Premultiplied - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, -#if defined(QT_QWS_DEPTH_15) && defined(QT_QWS_DEPTH_16) - convert_generic, -#else - 0, -#endif - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_RGB555 - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_ARGB8555_Premultiplied - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_RGB888 - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_RGB444 - - { - 0, - 0, - 0, - 0, - convert_generic, - convert_generic, - convert_generic, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - }, // Format_ARGB4444_Premultiplied - { - 0, - 0, - 0, - 0, - convert_RGBA_to_RGB, - convert_RGBA_to_ARGB, - convert_RGBA_to_ARGB_PM, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - mask_alpha_converter_RGBx, - mask_alpha_converter_RGBx, - }, // Format_RGBX8888 - { - 0, - 0, - 0, - 0, - convert_RGBA_to_RGB, - convert_RGBA_to_ARGB, - convert_RGBA_to_ARGB_PM, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - mask_alpha_converter_RGBx, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - 0, - convert_ARGB_to_ARGB_PM, -#else - 0, - 0 -#endif - }, // Format_RGBA8888 - - { - 0, - 0, - 0, - 0, - convert_RGBA_PM_to_RGB, - convert_RGBA_PM_to_ARGB, - convert_RGBA_to_ARGB, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - convert_ARGB_PM_to_RGB, - convert_ARGB_PM_to_ARGB, - 0, -#else - 0, - 0, - 0 -#endif - } // Format_RGBA8888_Premultiplied -}; - -static InPlace_Image_Converter inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = -{ - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_Mono - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_MonoLSB - { - 0, - 0, - 0, - 0, - 0, - convert_indexed8_to_RGB_inplace, - convert_indexed8_to_ARGB_PM_inplace, - convert_indexed8_to_RGB16_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }, // Format_Indexed8 - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - convert_RGB_to_RGB16_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }, // Format_RGB32 - { - 0, - 0, - 0, - 0, - 0, - 0, - convert_ARGB_to_ARGB_PM_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - convert_ARGB_to_RGBA_inplace, - 0, - }, // Format_ARGB32 - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - convert_ARGB_to_RGBA_inplace - }, // Format_ARGB32_Premultiplied - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_RGB16 - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_ARGB8565_Premultiplied - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_RGB666 - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_ARGB6666_Premultiplied - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_RGB555 - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_ARGB8555_Premultiplied - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_RGB888 - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_RGB444 - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }, // Format_ARGB4444_Premultiplied - { - 0, - 0, - 0, - 0, - 0, - convert_RGBA_to_ARGB_inplace, - convert_RGBA_to_ARGB_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }, // Format_RGBX8888 - { - 0, - 0, - 0, - 0, - 0, - 0, - convert_RGBA_to_ARGB_inplace, - convert_RGBA_to_ARGB_PM_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }, // Format_RGBA8888 - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - convert_RGBA_to_ARGB_inplace, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - } // Format_RGBA8888_Premultiplied -}; - -void qInitImageConversions() -{ -#ifdef QT_COMPILER_SUPPORTS_AVX - if (qCpuHasFeature(AVX)) { - extern bool convert_ARGB_to_ARGB_PM_inplace_avx(QImageData *data, Qt::ImageConversionFlags); - inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_avx; - - extern void convert_RGB888_to_RGB32_avx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_avx; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_avx; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_avx; - return; - } -#endif - -#if defined(QT_COMPILER_SUPPORTS_SSE2) && !defined(__AVX__) - if (qCpuHasFeature(SSE2)) { - extern bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags); - inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_sse2; -#ifdef QT_COMPILER_SUPPORTS_SSSE3 - if (qCpuHasFeature(SSSE3)) { - extern void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3; - } -#endif - return; - } -#endif // SSE2 - -#ifdef QT_COMPILER_SUPPORTS_NEON - if (qCpuHasFeature(NEON)) { - extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); - converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_neon; - converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_neon; - return; - } -#endif - -#ifdef QT_COMPILER_SUPPORTS_MIPS_DSPR2 - extern bool convert_ARGB_to_ARGB_PM_inplace_mips_dspr2(QImageData *data, Qt::ImageConversionFlags); - inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_mips_dspr2; - return; -#endif -} - -extern const uchar *qt_pow_rgb_gamma(); - -void qGamma_correct_back_to_linear_cs(QImage *image) -{ - const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); - if (!tables) - return; - const uchar *gamma = tables->qt_pow_rgb_gamma; - // gamma correct the pixels back to linear color space... - int h = image->height(); - int w = image->width(); - - for (int y=0; y<h; ++y) { - uint *pixels = (uint *) image->scanLine(y); - for (int x=0; x<w; ++x) { - uint p = pixels[x]; - uint r = gamma[qRed(p)]; - uint g = gamma[qGreen(p)]; - uint b = gamma[qBlue(p)]; - pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; - } - } -} - /*! Returns a copy of the image in the given \a format. @@ -4008,8 +1877,9 @@ QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) co if (format == Format_Invalid || d->format == Format_Invalid) return QImage(); - const Image_Converter *converterPtr = &converter_map[d->format][format]; - Image_Converter converter = *converterPtr; + Image_Converter converter = qimage_converter_map[d->format][format]; + if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) + converter = convert_generic; if (converter) { QImage image(d->width, d->height, format); @@ -4025,15 +1895,13 @@ QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) co return image; } + // Convert indexed formats over ARGB32 to the final format. Q_ASSERT(format != QImage::Format_ARGB32); Q_ASSERT(d->format != QImage::Format_ARGB32); - QImage image = convertToFormat(Format_ARGB32, flags); - return image.convertToFormat(format, flags); + return convertToFormat(Format_ARGB32, flags).convertToFormat(format, flags); } - - static inline int pixel_distance(QRgb p1, QRgb p2) { int r1 = qRed(p1); int g1 = qGreen(p1); @@ -4131,7 +1999,7 @@ QImage QImage::convertToFormat(Format format, const QVector<QRgb> &colorTable, Q return convertWithPalette(*this, format, colorTable); } - const Image_Converter *converterPtr = &converter_map[d->format][format]; + const Image_Converter *converterPtr = &qimage_converter_map[d->format][format]; Image_Converter converter = *converterPtr; if (!converter) return QImage(); @@ -4810,6 +2678,7 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const */ /*! + \fn QImage QImage::mirrored(bool horizontal = false, bool vertical = true) const Returns a mirror of the image, mirrored in the horizontal and/or the vertical direction depending on whether \a horizontal and \a vertical are set to true or false. @@ -4818,7 +2687,69 @@ QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const \sa {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::mirrored(bool horizontal, bool vertical) const + +template<typename T> +inline void mirrored_helper_loop(int w, int h, int dxi, int dxs, int dyi, int dy, const uchar* sdata, uchar* ddata, int sbpl, int dbpl) +{ + for (int sy = 0; sy < h; sy++, dy += dyi) { + const T* ssl = (T*)(sdata + sy*sbpl); + T* dsl = (T*)(ddata + dy*dbpl); + int dx = dxs; + for (int sx = 0; sx < w; sx++, dx += dxi) + dsl[dx] = ssl[sx]; + } +} + +template<typename T> +inline void mirrored_helper_loop_inplace(int w, int h, int dxi, int dxs, int dyi, int dy, uchar* sdata, int sbpl) +{ + for (int sy = 0; sy < h; sy++, dy += dyi) { + T* ssl = (T*)(sdata + sy*sbpl); + T* dsl = (T*)(sdata + dy*sbpl); + int dx = dxs; + for (int sx = 0; sx < w; sx++, dx += dxi) + std::swap(dsl[dx], ssl[sx]); + } +} + +inline void mirror_horizonal_bitmap(int w, int h, int dxs, uchar* data, int bpl, bool monolsb) +{ + int shift = w % 8; + for (int y = h-1; y >= 0; y--) { + quint8* a0 = (quint8*)(data + y*bpl); + // Swap bytes + quint8* a = a0+dxs; + while (a >= a0) { + *a = qt_get_bitflip_array()[*a]; + a--; + } + // Shift bits if unaligned + if (shift != 0) { + a = a0+dxs; + quint8 c = 0; + if (monolsb) { + while (a >= a0) { + quint8 nc = *a << shift; + *a = (*a >> (8-shift)) | c; + --a; + c = nc; + } + } else { + while (a >= a0) { + quint8 nc = *a >> shift; + *a = (*a << (8-shift)) | c; + --a; + c = nc; + } + } + } + } +} + +/*! + \internal +*/ +QImage QImage::mirrored_helper(bool horizontal, bool vertical) const { if (!d) return QImage(); @@ -4840,92 +2771,80 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const result.d->has_alpha_clut = d->has_alpha_clut; result.d->devicePixelRatio = d->devicePixelRatio; - if (depth() == 1) + if (d->depth == 1) w = (w+7)/8; int dxi = horizontal ? -1 : 1; int dxs = horizontal ? w-1 : 0; int dyi = vertical ? -1 : 1; - int dy = vertical ? h-1: 0; + int dys = vertical ? h-1 : 0; // 1 bit, 8 bit - if (d->depth == 1 || d->depth == 8) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - quint8* ssl = (quint8*)(d->data + sy*d->bytes_per_line); - quint8* dsl = (quint8*)(result.d->data + dy*result.d->bytes_per_line); - int dx = dxs; - for (int sx = 0; sx < w; sx++, dx += dxi) - dsl[dx] = ssl[sx]; - } - } + if (d->depth == 1 || d->depth == 8) + mirrored_helper_loop<quint8>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 16 bit - else if (d->depth == 16) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - quint16* ssl = (quint16*)(d->data + sy*d->bytes_per_line); - quint16* dsl = (quint16*)(result.d->data + dy*result.d->bytes_per_line); - int dx = dxs; - for (int sx = 0; sx < w; sx++, dx += dxi) - dsl[dx] = ssl[sx]; - } - } + else if (d->depth == 16) + mirrored_helper_loop<quint16>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 24 bit - else if (d->depth == 24) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line); - quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line); - int dx = dxs; - for (int sx = 0; sx < w; sx++, dx += dxi) - dsl[dx] = ssl[sx]; - } - } + else if (d->depth == 24) + mirrored_helper_loop<quint24>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // 32 bit - else if (d->depth == 32) { - for (int sy = 0; sy < h; sy++, dy += dyi) { - quint32* ssl = (quint32*)(d->data + sy*d->bytes_per_line); - quint32* dsl = (quint32*)(result.d->data + dy*result.d->bytes_per_line); - int dx = dxs; - for (int sx = 0; sx < w; sx++, dx += dxi) - dsl[dx] = ssl[sx]; - } - } + else if (d->depth == 32) + mirrored_helper_loop<quint32>(w, h, dxi, dxs, dyi, dys, d->data, result.d->data, d->bytes_per_line, result.d->bytes_per_line); // special handling of 1 bit images for horizontal mirroring - if (horizontal && d->depth == 1) { - int shift = width() % 8; - for (int y = h-1; y >= 0; y--) { - quint8* a0 = (quint8*)(result.d->data + y*d->bytes_per_line); - // Swap bytes - quint8* a = a0+dxs; - while (a >= a0) { - *a = bitflip[*a]; - a--; - } - // Shift bits if unaligned - if (shift != 0) { - a = a0+dxs; - quint8 c = 0; - if (format() == Format_MonoLSB) { - while (a >= a0) { - quint8 nc = *a << shift; - *a = (*a >> (8-shift)) | c; - --a; - c = nc; - } - } else { - while (a >= a0) { - quint8 nc = *a >> shift; - *a = (*a << (8-shift)) | c; - --a; - c = nc; - } - } - } - } - } - + if (horizontal && d->depth == 1) + mirror_horizonal_bitmap(d->width, d->height, dxs, result.d->data, result.d->bytes_per_line, d->format == Format_MonoLSB); return result; } /*! + \internal +*/ +void QImage::mirrored_inplace(bool horizontal, bool vertical) +{ + if (!d) + return; + + if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical)) + return; + + detach(); + + int w = d->width; + int h = d->height; + + if (d->depth == 1) + w = (w+7)/8; + int dxi = horizontal ? -1 : 1; + int dxs = horizontal ? w-1 : 0; + int dyi = vertical ? -1 : 1; + int dys = vertical ? h-1 : 0; + + if (vertical) + h = h/2; + else if (horizontal) + w = w/2; + + // 1 bit, 8 bit + if (d->depth == 1 || d->depth == 8) + mirrored_helper_loop_inplace<quint8>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 16 bit + else if (d->depth == 16) + mirrored_helper_loop_inplace<quint16>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 24 bit + else if (d->depth == 24) + mirrored_helper_loop_inplace<quint24>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + // 32 bit + else if (d->depth == 32) + mirrored_helper_loop_inplace<quint32>(w, h, dxi, dxs, dyi, dys, d->data, d->bytes_per_line); + + // special handling of 1 bit images for horizontal mirroring + if (horizontal && d->depth == 1) + mirror_horizonal_bitmap(d->width, d->height, dxs, d->data, d->bytes_per_line, d->format == Format_MonoLSB); +} + +/*! + \fn QImage QImage::rgbSwapped() const Returns a QImage in which the values of the red and blue components of all pixels have been swapped, effectively converting an RGB image to an BGR image. @@ -4934,16 +2853,54 @@ QImage QImage::mirrored(bool horizontal, bool vertical) const \sa {QImage#Image Transformations}{Image Transformations} */ -QImage QImage::rgbSwapped() const + +inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout) +{ + Q_ASSERT(layout->redWidth == layout->blueWidth); + FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; + StorePixelsFunc store = qStorePixels[layout->bpp]; + + const uint redBlueMask = (1 << layout->redWidth) - 1; + const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift) + | (((1 << layout->greenWidth) - 1) << layout->greenShift); + + const int buffer_size = 2048; + uint buffer[buffer_size]; + for (int i = 0; i < height; ++i) { + uchar *q = dst->scanLine(i); + const uchar *p = src->constScanLine(i); + int x = 0; + while (x < width) { + int l = qMin(width - x, buffer_size); + const uint *ptr = fetch(buffer, p, x, l); + for (int j = 0; j < l; ++j) { + uint red = (ptr[j] >> layout->redShift) & redBlueMask; + uint blue = (ptr[j] >> layout->blueShift) & redBlueMask; + buffer[j] = (ptr[j] & alphaGreenMask) + | (red << layout->blueShift) + | (blue << layout->redShift); + } + store(q, buffer, x, l); + x += l; + } + } +} + +/*! + \internal +*/ +QImage QImage::rgbSwapped_helper() const { if (isNull()) return *this; + QImage res; + switch (d->format) { case Format_Invalid: case NImageFormats: Q_ASSERT(false); - return res; + break; case Format_Mono: case Format_MonoLSB: case Format_Indexed8: @@ -4952,7 +2909,7 @@ QImage QImage::rgbSwapped() const QRgb c = res.d->colortable.at(i); res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00)); } - return res; + break; case Format_RGB32: case Format_ARGB32: case Format_ARGB32_Premultiplied: @@ -4965,15 +2922,16 @@ QImage QImage::rgbSwapped() const QIMAGE_SANITYCHECK_MEMORY(res); for (int i = 0; i < d->height; i++) { uint *q = (uint*)res.scanLine(i); - uint *p = (uint*)constScanLine(i); - uint *end = p + d->width; + const uint *p = (const uint*)constScanLine(i); + const uint *end = p + d->width; while (p < end) { - *q = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + uint c = *p; + *q = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00); p++; q++; } } - return res; + break; case Format_RGB16: res = QImage(d->width, d->height, d->format); QIMAGE_SANITYCHECK_MEMORY(res); @@ -4982,48 +2940,77 @@ QImage QImage::rgbSwapped() const const ushort *p = (const ushort*)constScanLine(i); const ushort *end = p + d->width; while (p < end) { - *q = ((*p << 11) & 0xf800) | ((*p >> 11) & 0x1f) | (*p & 0x07e0); + ushort c = *p; + *q = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0); p++; q++; } } - return res; + break; default: + res = QImage(d->width, d->height, d->format); + rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]); break; } + return res; +} - res = QImage(d->width, d->height, d->format); - QIMAGE_SANITYCHECK_MEMORY(res); - const QPixelLayout *layout = &qPixelLayouts[d->format]; - Q_ASSERT(layout->redWidth == layout->blueWidth); - FetchPixelsFunc fetch = qFetchPixels[layout->bpp]; - StorePixelsFunc store = qStorePixels[layout->bpp]; +/*! + \internal +*/ +void QImage::rgbSwapped_inplace() +{ + if (isNull()) + return; - const uint redBlueMask = (1 << layout->redWidth) - 1; - const uint alphaGreenMask = (((1 << layout->alphaWidth) - 1) << layout->alphaShift) - | (((1 << layout->greenWidth) - 1) << layout->greenShift); + detach(); - const int buffer_size = 2048; - uint buffer[buffer_size]; - for (int i = 0; i < d->height; ++i) { - uchar *q = res.scanLine(i); - const uchar *p = constScanLine(i); - int x = 0; - while (x < d->width) { - int l = qMin(d->width - x, buffer_size); - const uint *ptr = fetch(buffer, p, x, l); - for (int j = 0; j < l; ++j) { - uint red = (ptr[j] >> layout->redShift) & redBlueMask; - uint blue = (ptr[j] >> layout->blueShift) & redBlueMask; - buffer[j] = (ptr[j] & alphaGreenMask) - | (red << layout->blueShift) - | (blue << layout->redShift); + switch (d->format) { + case Format_Invalid: + case NImageFormats: + Q_ASSERT(false); + break; + case Format_Mono: + case Format_MonoLSB: + case Format_Indexed8: + for (int i = 0; i < d->colortable.size(); i++) { + QRgb c = d->colortable.at(i); + d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00)); + } + break; + case Format_RGB32: + case Format_ARGB32: + case Format_ARGB32_Premultiplied: +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + case Format_RGBX8888: + case Format_RGBA8888: + case Format_RGBA8888_Premultiplied: +#endif + for (int i = 0; i < d->height; i++) { + uint *p = (uint*)scanLine(i); + uint *end = p + d->width; + while (p < end) { + uint c = *p; + *p = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00); + p++; + } + } + break; + case Format_RGB16: + for (int i = 0; i < d->height; i++) { + ushort *p = (ushort*)scanLine(i); + ushort *end = p + d->width; + while (p < end) { + ushort c = *p; + *p = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0); + p++; } - store(q, buffer, x, l); - x += l; } + break; + default: + rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]); + break; } - return res; } /*! @@ -6433,10 +4420,12 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla if (ref.load() > 1) return false; - const InPlace_Image_Converter *const converterPtr = &inplace_converter_map[format][newFormat]; + const InPlace_Image_Converter *const converterPtr = &qimage_inplace_converter_map[format][newFormat]; InPlace_Image_Converter converter = *converterPtr; if (converter) return converter(this, flags); + else if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8) + return convert_generic_inplace(this, newFormat, flags); else return false; } diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index bc7f3729ad..e8f195c18e 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -245,8 +245,19 @@ public: static QMatrix trueMatrix(const QMatrix &, int w, int h); QImage transformed(const QTransform &matrix, Qt::TransformationMode mode = Qt::FastTransformation) const; static QTransform trueMatrix(const QTransform &, int w, int h); +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QIMAGE_COMPAT_CPP) + QImage mirrored(bool horizontally = false, bool vertically = true) const & + { return mirrored_helper(horizontally, vertically); } + QImage &&mirrored(bool horizontally = false, bool vertically = true) && + { mirrored_inplace(horizontally, vertically); return qMove(*this); } + QImage rgbSwapped() const & + { return rgbSwapped_helper(); } + QImage &&rgbSwapped() && + { rgbSwapped_inplace(); return qMove(*this); } +#else QImage mirrored(bool horizontally = false, bool vertically = true) const; QImage rgbSwapped() const; +#endif void invertPixels(InvertMode = InvertRgb); @@ -298,6 +309,10 @@ public: protected: virtual int metric(PaintDeviceMetric metric) const; + QImage mirrored_helper(bool horizontal, bool vertical) const; + QImage rgbSwapped_helper() const; + void mirrored_inplace(bool horizontal, bool vertical); + void rgbSwapped_inplace(); private: friend class QWSOnScreenSurface; diff --git a/src/gui/painting/qdrawhelper_avx.cpp b/src/gui/image/qimage_compat.cpp index 7da6ce6a20..9886d392fb 100644 --- a/src/gui/painting/qdrawhelper_avx.cpp +++ b/src/gui/image/qimage_compat.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2012 Intel Corporation +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -39,32 +39,24 @@ ** ****************************************************************************/ -#include <private/qsimd_p.h> - -#ifdef QT_COMPILER_SUPPORTS_AVX -#define QDRAWHELPER_AVX - -#ifndef __AVX__ -#error "AVX not enabled in this file, cannot proceed" +#ifdef QIMAGE_H +# error "This file cannot be used with precompiled headers" #endif +#define QT_COMPILING_QIMAGE_COMPAT_CPP -#define qt_blend_argb32_on_argb32_ssse3 qt_blend_argb32_on_argb32_avx -#include "qdrawhelper_ssse3.cpp" +#include "qimage.h" -//#define qt_blend_argb32_on_argb32_sse2 qt_blend_argb32_on_argb32_avx -#define qt_blend_rgb32_on_rgb32_sse2 qt_blend_rgb32_on_rgb32_avx -#define comp_func_SourceOver_sse2 comp_func_SourceOver_avx -#define comp_func_Plus_sse2 comp_func_Plus_avx -#define comp_func_Source_sse2 comp_func_Source_avx -#define comp_func_solid_SourceOver_sse2 comp_func_solid_SourceOver_avx -#define qt_memfill32_sse2 qt_memfill32_avx -#define qt_memfill16_sse2 qt_memfill16_avx -#define qt_bitmapblit32_sse2 qt_bitmapblit32_avx -#define qt_bitmapblit16_sse2 qt_bitmapblit16_avx -#define QSimdSse2 QSimdAvx -#define qt_fetch_radial_gradient_sse2 qt_fetch_radial_gradient_avx -#define qt_scale_image_argb32_on_argb32_sse2 qt_scale_image_argb32_on_argb32_avx +QT_BEGIN_NAMESPACE -#include "qdrawhelper_sse2.cpp" +// These implementations must be the same as the inline versions in qimage.h +QImage QImage::mirrored(bool horizontally, bool vertically) const +{ + return mirrored_helper(horizontally, vertically); +} -#endif +QImage QImage::rgbSwapped() const +{ + return rgbSwapped_helper(); +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp new file mode 100644 index 0000000000..9b79f4ccc0 --- /dev/null +++ b/src/gui/image/qimage_conversions.cpp @@ -0,0 +1,2183 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qdrawhelper_p.h> +#include <private/qguiapplication_p.h> +#include <private/qsimd_p.h> + +#include <private/qimage_p.h> + +QT_BEGIN_NAMESPACE + +// table to flip bits +static const uchar bitflip[256] = { + /* + open OUT, "| fmt"; + for $i (0..255) { + print OUT (($i >> 7) & 0x01) | (($i >> 5) & 0x02) | + (($i >> 3) & 0x04) | (($i >> 1) & 0x08) | + (($i << 7) & 0x80) | (($i << 5) & 0x40) | + (($i << 3) & 0x20) | (($i << 1) & 0x10), ", "; + } + close OUT; + */ + 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, + 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, + 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, + 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, + 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, + 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, + 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, + 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, + 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, + 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, + 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, + 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, + 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, + 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, + 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, + 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255 +}; + +const uchar *qt_get_bitflip_array() // called from QPixmap code +{ + return bitflip; +} + +void qGamma_correct_back_to_linear_cs(QImage *image) +{ + const QDrawHelperGammaTables *tables = QGuiApplicationPrivate::instance()->gammaTables(); + if (!tables) + return; + const uchar *gamma = tables->qt_pow_rgb_gamma; + // gamma correct the pixels back to linear color space... + int h = image->height(); + int w = image->width(); + + for (int y=0; y<h; ++y) { + uint *pixels = (uint *) image->scanLine(y); + for (int x=0; x<w; ++x) { + uint p = pixels[x]; + uint r = gamma[qRed(p)]; + uint g = gamma[qGreen(p)]; + uint b = gamma[qBlue(p)]; + pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; + } + } +} + +/***************************************************************************** + Internal routines for converting image depth. + *****************************************************************************/ + +// Cannot be used with indexed formats. +void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(dest->format > QImage::Format_Indexed8); + Q_ASSERT(src->format > QImage::Format_Indexed8); + const int buffer_size = 2048; + uint buffer[buffer_size]; + const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; + const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; + const uchar *srcData = src->data; + uchar *destData = dest->data; + + FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp]; + StorePixelsFunc store = qStorePixels[destLayout->bpp]; + + for (int y = 0; y < src->height; ++y) { + int x = 0; + while (x < src->width) { + int l = qMin(src->width - x, buffer_size); + const uint *ptr = fetch(buffer, srcData, x, l); + ptr = srcLayout->convertToARGB32PM(buffer, ptr, l, srcLayout, 0); + ptr = destLayout->convertFromARGB32PM(buffer, ptr, l, destLayout, 0); + store(destData, ptr, x, l); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } +} + +// Cannot be used with indexed formats or between formats with different pixel depths. +bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags) +{ + Q_ASSERT(dst_format > QImage::Format_Indexed8); + Q_ASSERT(data->format > QImage::Format_Indexed8); + if (data->depth != qt_depthForFormat(dst_format)) + return false; + + const int buffer_size = 2048; + uint buffer[buffer_size]; + const QPixelLayout *srcLayout = &qPixelLayouts[data->format]; + const QPixelLayout *destLayout = &qPixelLayouts[dst_format]; + + uchar *srcData = data->data; + + FetchPixelsFunc fetch = qFetchPixels[srcLayout->bpp]; + StorePixelsFunc store = qStorePixels[destLayout->bpp]; + + for (int y = 0; y < data->height; ++y) { + int x = 0; + while (x < data->width) { + int l = qMin(data->width - x, buffer_size); + const uint *ptr = fetch(buffer, srcData, x, l); + ptr = srcLayout->convertToARGB32PM(buffer, ptr, l, srcLayout, 0); + ptr = destLayout->convertFromARGB32PM(buffer, ptr, l, destLayout, 0); + store(srcData, ptr, x, l); + x += l; + } + srcData += data->bytes_per_line; + } + data->format = dst_format; + return true; +} + +static void convert_ARGB_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888); + Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied || dest->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = PREMUL(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +extern bool convert_ARGB_to_ARGB_PM_inplace_sse2(QImageData *data, Qt::ImageConversionFlags); + +#ifndef __SSE2__ +static bool convert_ARGB_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_ARGB32); + + const int pad = (data->bytes_per_line >> 2) - data->width; + QRgb *rgb_data = (QRgb *) data->data; + + for (int i = 0; i < data->height; ++i) { + const QRgb *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = PREMUL(*rgb_data); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_ARGB32_Premultiplied; + return true; +} +#endif + +static void convert_ARGB_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32); + Q_ASSERT(dest->format == QImage::Format_RGBX8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const quint32 *src_data = (quint32 *) src->data; + quint32 *dest_data = (quint32 *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint32 *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(0xff000000 | *src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_ARGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const quint32 *src_data = (quint32 *) src->data; + quint32 *dest_data = (quint32 *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint32 *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_ARGB_to_RGBA_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_ARGB32_Premultiplied); + + const int pad = (data->bytes_per_line >> 2) - data->width; + quint32 *rgb_data = (quint32 *) data->data; + + for (int i = 0; i < data->height; ++i) { + const quint32 *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = ARGB2RGBA(*rgb_data); + ++rgb_data; + } + rgb_data += pad; + } + if (data->format == QImage::Format_ARGB32) + data->format = QImage::Format_RGBA8888; + else + data->format = QImage::Format_RGBA8888_Premultiplied; + return true; +} + +static void convert_ARGB_to_RGBA_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32); + Q_ASSERT(dest->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const quint32 *src_data = (quint32 *) src->data; + quint32 *dest_data = (quint32 *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint32 *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(PREMUL(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBX8888 || src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const quint32 *src_data = (quint32 *) src->data; + quint32 *dest_data = (quint32 *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint32 *end = src_data + src->width; + while (src_data < end) { + *dest_data = RGBA2ARGB(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888 || data->format == QImage::Format_RGBA8888_Premultiplied); + + const int pad = (data->bytes_per_line >> 2) - data->width; + QRgb *rgb_data = (QRgb *) data->data; + + for (int i = 0; i < data->height; ++i) { + const QRgb *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = RGBA2ARGB(*rgb_data); + ++rgb_data; + } + rgb_data += pad; + } + if (data->format == QImage::Format_RGBA8888_Premultiplied) + data->format = QImage::Format_ARGB32_Premultiplied; + else if (data->format == QImage::Format_RGBX8888) + data->format = QImage::Format_RGB32; + else + data->format = QImage::Format_ARGB32; + return true; +} + +static void convert_RGBA_to_ARGB_PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA8888); + Q_ASSERT(dest->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const quint32 *src_data = (quint32 *) src->data; + quint32 *dest_data = (quint32 *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const quint32 *end = src_data + src->width; + while (src_data < end) { + *dest_data = PREMUL(RGBA2ARGB(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA8888); + + const int pad = (data->bytes_per_line >> 2) - data->width; + QRgb *rgb_data = (QRgb *) data->data; + + for (int i = 0; i < data->height; ++i) { + const QRgb *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = PREMUL(RGBA2ARGB(*rgb_data)); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_ARGB32_Premultiplied; + return true; +} + +static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_Indexed8); + const int depth = 32; + + const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; + const int nbytes = dst_bytes_per_line * data->height; + uchar *const newData = (uchar *)realloc(data->data, nbytes); + if (!newData) + return false; + + data->data = newData; + + // start converting from the end because the end image is bigger than the source + uchar *src_data = newData + data->nbytes; // end of src + quint32 *dest_data = (quint32 *) (newData + nbytes); // end of dest > end of src + const int width = data->width; + const int src_pad = data->bytes_per_line - width; + const int dest_pad = (dst_bytes_per_line >> 2) - width; + if (data->colortable.size() == 0) { + data->colortable.resize(256); + for (int i = 0; i < 256; ++i) + data->colortable[i] = qRgb(i, i, i); + } else { + for (int i = 0; i < data->colortable.size(); ++i) + data->colortable[i] = PREMUL(data->colortable.at(i)); + + // Fill the rest of the table in case src_data > colortable.size() + const int oldSize = data->colortable.size(); + const QRgb lastColor = data->colortable.at(oldSize - 1); + data->colortable.insert(oldSize, 256 - oldSize, lastColor); + } + + for (int i = 0; i < data->height; ++i) { + src_data -= src_pad; + dest_data -= dest_pad; + for (int pixI = 0; pixI < width; ++pixI) { + --src_data; + --dest_data; + *dest_data = data->colortable.at(*src_data); + } + } + + data->colortable = QVector<QRgb>(); + data->format = QImage::Format_ARGB32_Premultiplied; + data->bytes_per_line = dst_bytes_per_line; + data->depth = depth; + data->nbytes = nbytes; + + return true; +} + +static bool convert_indexed8_to_RGB_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_Indexed8); + const int depth = 32; + + const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; + const int nbytes = dst_bytes_per_line * data->height; + uchar *const newData = (uchar *)realloc(data->data, nbytes); + if (!newData) + return false; + + data->data = newData; + + // start converting from the end because the end image is bigger than the source + uchar *src_data = newData + data->nbytes; + quint32 *dest_data = (quint32 *) (newData + nbytes); + const int width = data->width; + const int src_pad = data->bytes_per_line - width; + const int dest_pad = (dst_bytes_per_line >> 2) - width; + if (data->colortable.size() == 0) { + data->colortable.resize(256); + for (int i = 0; i < 256; ++i) + data->colortable[i] = qRgb(i, i, i); + } else { + // Fill the rest of the table in case src_data > colortable.size() + const int oldSize = data->colortable.size(); + const QRgb lastColor = data->colortable.at(oldSize - 1); + data->colortable.insert(oldSize, 256 - oldSize, lastColor); + } + + for (int i = 0; i < data->height; ++i) { + src_data -= src_pad; + dest_data -= dest_pad; + for (int pixI = 0; pixI < width; ++pixI) { + --src_data; + --dest_data; + *dest_data = (quint32) data->colortable.at(*src_data); + } + } + + data->colortable = QVector<QRgb>(); + data->format = QImage::Format_RGB32; + data->bytes_per_line = dst_bytes_per_line; + data->depth = depth; + data->nbytes = nbytes; + + return true; +} + +static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_Indexed8); + const int depth = 16; + + const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; + const int nbytes = dst_bytes_per_line * data->height; + uchar *const newData = (uchar *)realloc(data->data, nbytes); + if (!newData) + return false; + + data->data = newData; + + // start converting from the end because the end image is bigger than the source + uchar *src_data = newData + data->nbytes; + quint16 *dest_data = (quint16 *) (newData + nbytes); + const int width = data->width; + const int src_pad = data->bytes_per_line - width; + const int dest_pad = (dst_bytes_per_line >> 1) - width; + + quint16 colorTableRGB16[256]; + if (data->colortable.isEmpty()) { + for (int i = 0; i < 256; ++i) + colorTableRGB16[i] = qConvertRgb32To16(qRgb(i, i, i)); + } else { + // 1) convert the existing colors to RGB16 + const int tableSize = data->colortable.size(); + for (int i = 0; i < tableSize; ++i) + colorTableRGB16[i] = qConvertRgb32To16(data->colortable.at(i)); + data->colortable = QVector<QRgb>(); + + // 2) fill the rest of the table in case src_data > colortable.size() + const quint16 lastColor = colorTableRGB16[tableSize - 1]; + for (int i = tableSize; i < 256; ++i) + colorTableRGB16[i] = lastColor; + } + + for (int i = 0; i < data->height; ++i) { + src_data -= src_pad; + dest_data -= dest_pad; + for (int pixI = 0; pixI < width; ++pixI) { + --src_data; + --dest_data; + *dest_data = colorTableRGB16[*src_data]; + } + } + + data->format = QImage::Format_RGB16; + data->bytes_per_line = dst_bytes_per_line; + data->depth = depth; + data->nbytes = nbytes; + + return true; +} + +static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGB32); + const int depth = 16; + + const int dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; + const int src_bytes_per_line = data->bytes_per_line; + quint32 *src_data = (quint32 *) data->data; + quint16 *dst_data = (quint16 *) data->data; + + for (int i = 0; i < data->height; ++i) { + for (int j = 0; j < data->width; ++j) + dst_data[j] = qConvertRgb32To16(src_data[j]); + src_data = (quint32 *) (((char*)src_data) + src_bytes_per_line); + dst_data = (quint16 *) (((char*)dst_data) + dst_bytes_per_line); + } + data->format = QImage::Format_RGB16; + data->bytes_per_line = dst_bytes_per_line; + data->depth = depth; + data->nbytes = dst_bytes_per_line * data->height; + uchar *const newData = (uchar *)realloc(data->data, data->nbytes); + if (newData) { + data->data = newData; + return true; + } else { + return false; + } +} + +static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = INV_PREMUL(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_ARGB_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_RGBX8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = 0xff000000 | INV_PREMUL(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_ARGB_PM_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBX8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(0xff000000 | INV_PREMUL(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_ARGB_PM_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(INV_PREMUL(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBX8888); + Q_ASSERT(dest->format == QImage::Format_RGB32); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const uint *src_data = (const uint *)src->data; + uint *dest_data = (uint *)dest->data; + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + *dest_data = RGBA2ARGB(*src_data) | 0xff000000; + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGB32); + Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const uint *src_data = (const uint *)src->data; + uint *dest_data = (uint *)dest->data; + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + *dest_data = ARGB2RGBA(*src_data | 0xff000000); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_ARGB32); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = INV_PREMUL(RGBA2ARGB(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA_PM_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA8888_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGB32); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgb *src_data = (QRgb *) src->data; + QRgb *dest_data = (QRgb *) dest->data; + + for (int i = 0; i < src->height; ++i) { + const QRgb *end = src_data + src->width; + while (src_data < end) { + *dest_data = 0xff000000 | INV_PREMUL(RGBA2ARGB(*src_data)); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void swap_bit_order(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); + Q_ASSERT(dest->format == QImage::Format_Mono || dest->format == QImage::Format_MonoLSB); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + Q_ASSERT(src->nbytes == dest->nbytes); + Q_ASSERT(src->bytes_per_line == dest->bytes_per_line); + + dest->colortable = src->colortable; + + const uchar *src_data = src->data; + const uchar *end = src->data + src->nbytes; + uchar *dest_data = dest->data; + while (src_data < end) { + *dest_data = bitflip[*src_data]; + ++src_data; + ++dest_data; + } +} + +static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const uint *src_data = (const uint *)src->data; + uint *dest_data = (uint *)dest->data; + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + *dest_data = *src_data | 0xff000000; + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void mask_alpha_converter_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags) +{ +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + return mask_alpha_converter(dest, src, flags); +#else + Q_UNUSED(flags); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const uint *src_data = (const uint *)src->data; + uint *dest_data = (uint *)dest->data; + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + *dest_data = *src_data | 0x000000ff; + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +#endif +} + +static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) +{ + QVector<QRgb> colorTable = ctbl; + if (format == QImage::Format_RGB32) { + // check if the color table has alpha + for (int i = 0; i < colorTable.size(); ++i) + if (qAlpha(colorTable.at(i) != 0xff)) + colorTable[i] = colorTable.at(i) | 0xff000000; + } else if (format == QImage::Format_ARGB32_Premultiplied) { + // check if the color table has alpha + for (int i = 0; i < colorTable.size(); ++i) + colorTable[i] = PREMUL(colorTable.at(i)); + } + return colorTable; +} + +// +// dither_to_1: Uses selected dithering algorithm. +// + +void dither_to_Mono(QImageData *dst, const QImageData *src, + Qt::ImageConversionFlags flags, bool fromalpha) +{ + Q_ASSERT(src->width == dst->width); + Q_ASSERT(src->height == dst->height); + Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB); + + dst->colortable.clear(); + dst->colortable.append(0xffffffff); + dst->colortable.append(0xff000000); + + enum { Threshold, Ordered, Diffuse } dithermode; + + if (fromalpha) { + if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither) + dithermode = Diffuse; + else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither) + dithermode = Ordered; + else + dithermode = Threshold; + } else { + if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) + dithermode = Threshold; + else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither) + dithermode = Ordered; + else + dithermode = Diffuse; + } + + int w = src->width; + int h = src->height; + int d = src->depth; + uchar gray[256]; // gray map for 8 bit images + bool use_gray = (d == 8); + if (use_gray) { // make gray map + if (fromalpha) { + // Alpha 0x00 -> 0 pixels (white) + // Alpha 0xFF -> 1 pixels (black) + for (int i = 0; i < src->colortable.size(); i++) + gray[i] = (255 - (src->colortable.at(i) >> 24)); + } else { + // Pixel 0x00 -> 1 pixels (black) + // Pixel 0xFF -> 0 pixels (white) + for (int i = 0; i < src->colortable.size(); i++) + gray[i] = qGray(src->colortable.at(i)); + } + } + + uchar *dst_data = dst->data; + int dst_bpl = dst->bytes_per_line; + const uchar *src_data = src->data; + int src_bpl = src->bytes_per_line; + + switch (dithermode) { + case Diffuse: { + QScopedArrayPointer<int> lineBuffer(new int[w * 2]); + int *line1 = lineBuffer.data(); + int *line2 = lineBuffer.data() + w; + int bmwidth = (w+7)/8; + + int *b1, *b2; + int wbytes = w * (d/8); + const uchar *p = src->data; + const uchar *end = p + wbytes; + b2 = line2; + if (use_gray) { // 8 bit image + while (p < end) + *b2++ = gray[*p++]; + } else { // 32 bit image + if (fromalpha) { + while (p < end) { + *b2++ = 255 - (*(uint*)p >> 24); + p += 4; + } + } else { + while (p < end) { + *b2++ = qGray(*(uint*)p); + p += 4; + } + } + } + for (int y=0; y<h; y++) { // for each scan line... + int *tmp = line1; line1 = line2; line2 = tmp; + bool not_last_line = y < h - 1; + if (not_last_line) { // calc. grayvals for next line + p = src->data + (y+1)*src->bytes_per_line; + end = p + wbytes; + b2 = line2; + if (use_gray) { // 8 bit image + while (p < end) + *b2++ = gray[*p++]; + } else { // 24 bit image + if (fromalpha) { + while (p < end) { + *b2++ = 255 - (*(uint*)p >> 24); + p += 4; + } + } else { + while (p < end) { + *b2++ = qGray(*(uint*)p); + p += 4; + } + } + } + } + + int err; + uchar *p = dst->data + y*dst->bytes_per_line; + memset(p, 0, bmwidth); + b1 = line1; + b2 = line2; + int bit = 7; + for (int x=1; x<=w; x++) { + if (*b1 < 128) { // black pixel + err = *b1++; + *p |= 1 << bit; + } else { // white pixel + err = *b1++ - 255; + } + if (bit == 0) { + p++; + bit = 7; + } else { + bit--; + } + if (x < w) + *b1 += (err*7)>>4; // spread error to right pixel + if (not_last_line) { + b2[0] += (err*5)>>4; // pixel below + if (x > 1) + b2[-1] += (err*3)>>4; // pixel below left + if (x < w) + b2[1] += err>>4; // pixel below right + } + b2++; + } + } + } break; + case Ordered: { + + memset(dst->data, 0, dst->nbytes); + if (d == 32) { + for (int i=0; i<h; i++) { + const uint *p = (const uint *)src_data; + const uint *end = p + w; + uchar *m = dst_data; + int bit = 7; + int j = 0; + if (fromalpha) { + while (p < end) { + if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15]) + *m |= 1 << bit; + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + } else { + while (p < end) { + if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15]) + *m |= 1 << bit; + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + } + dst_data += dst_bpl; + src_data += src_bpl; + } + } else + /* (d == 8) */ { + for (int i=0; i<h; i++) { + const uchar *p = src_data; + const uchar *end = p + w; + uchar *m = dst_data; + int bit = 7; + int j = 0; + while (p < end) { + if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15]) + *m |= 1 << bit; + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + dst_data += dst_bpl; + src_data += src_bpl; + } + } + } break; + default: { // Threshold: + memset(dst->data, 0, dst->nbytes); + if (d == 32) { + for (int i=0; i<h; i++) { + const uint *p = (const uint *)src_data; + const uint *end = p + w; + uchar *m = dst_data; + int bit = 7; + if (fromalpha) { + while (p < end) { + if ((*p++ >> 24) >= 128) + *m |= 1 << bit; // Set mask "on" + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + } else { + while (p < end) { + if (qGray(*p++) < 128) + *m |= 1 << bit; // Set pixel "black" + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + } + dst_data += dst_bpl; + src_data += src_bpl; + } + } else + if (d == 8) { + for (int i=0; i<h; i++) { + const uchar *p = src_data; + const uchar *end = p + w; + uchar *m = dst_data; + int bit = 7; + while (p < end) { + if (gray[*p++] < 128) + *m |= 1 << bit; // Set mask "on"/ pixel "black" + if (bit == 0) { + m++; + bit = 7; + } else { + bit--; + } + } + dst_data += dst_bpl; + src_data += src_bpl; + } + } + } + } + + if (dst->format == QImage::Format_MonoLSB) { + // need to swap bit order + uchar *sl = dst->data; + int bpl = (dst->width + 7) * dst->depth / 8; + int pad = dst->bytes_per_line - bpl; + for (int y=0; y<dst->height; ++y) { + for (int x=0; x<bpl; ++x) { + *sl = bitflip[*sl]; + ++sl; + } + sl += pad; + } + } +} + +static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) +{ + dither_to_Mono(dst, src, flags, false); +} + +static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) +{ + QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); + convert_ARGB_PM_to_ARGB(tmp.data(), src, flags); + dither_to_Mono(dst, tmp.data(), flags, false); +} + +// +// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit +// image with a colormap. If the 32 bit image has more than 256 colors, +// we convert the red,green and blue bytes into a single byte encoded +// as 6 shades of each of red, green and blue. +// +// if dithering is needed, only 1 color at most is available for alpha. +// +struct QRgbMap { + inline QRgbMap() : used(0) { } + uchar pix; + uchar used; + QRgb rgb; +}; + +static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) +{ + Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32); + Q_ASSERT(dst->format == QImage::Format_Indexed8); + Q_ASSERT(src->width == dst->width); + Q_ASSERT(src->height == dst->height); + + bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither + || src->format == QImage::Format_ARGB32; + uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0; + + const int tablesize = 997; // prime + QRgbMap table[tablesize]; + int pix=0; + + if (!dst->colortable.isEmpty()) { + QVector<QRgb> ctbl = dst->colortable; + dst->colortable.resize(256); + // Preload palette into table. + // Almost same code as pixel insertion below + for (int i = 0; i < dst->colortable.size(); ++i) { + // Find in table... + QRgb p = ctbl.at(i) | alpha_mask; + int hash = p % tablesize; + for (;;) { + if (table[hash].used) { + if (table[hash].rgb == p) { + // Found previous insertion - use it + break; + } else { + // Keep searching... + if (++hash == tablesize) hash = 0; + } + } else { + // Cannot be in table + Q_ASSERT (pix != 256); // too many colors + // Insert into table at this unused position + dst->colortable[pix] = p; + table[hash].pix = pix++; + table[hash].rgb = p; + table[hash].used = 1; + break; + } + } + } + } + + if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) { + dst->colortable.resize(256); + const uchar *src_data = src->data; + uchar *dest_data = dst->data; + for (int y = 0; y < src->height; y++) { // check if <= 256 colors + const QRgb *s = (const QRgb *)src_data; + uchar *b = dest_data; + for (int x = 0; x < src->width; ++x) { + QRgb p = s[x] | alpha_mask; + int hash = p % tablesize; + for (;;) { + if (table[hash].used) { + if (table[hash].rgb == (p)) { + // Found previous insertion - use it + break; + } else { + // Keep searching... + if (++hash == tablesize) hash = 0; + } + } else { + // Cannot be in table + if (pix == 256) { // too many colors + do_quant = true; + // Break right out + x = src->width; + y = src->height; + } else { + // Insert into table at this unused position + dst->colortable[pix] = p; + table[hash].pix = pix++; + table[hash].rgb = p; + table[hash].used = 1; + } + break; + } + } + *b++ = table[hash].pix; // May occur once incorrectly + } + src_data += src->bytes_per_line; + dest_data += dst->bytes_per_line; + } + } + int numColors = do_quant ? 256 : pix; + + dst->colortable.resize(numColors); + + if (do_quant) { // quantization needed + +#define MAX_R 5 +#define MAX_G 5 +#define MAX_B 5 +#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b)) + + for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube + for (int gc=0; gc<=MAX_G; gc++) + for (int bc=0; bc<=MAX_B; bc++) + dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B); + + const uchar *src_data = src->data; + uchar *dest_data = dst->data; + if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) { + for (int y = 0; y < src->height; y++) { + const QRgb *p = (const QRgb *)src_data; + const QRgb *end = p + src->width; + uchar *b = dest_data; + + while (p < end) { +#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255)) + *b++ = + INDEXOF( + DITHER(qRed(*p), MAX_R), + DITHER(qGreen(*p), MAX_G), + DITHER(qBlue(*p), MAX_B) + ); +#undef DITHER + p++; + } + src_data += src->bytes_per_line; + dest_data += dst->bytes_per_line; + } + } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) { + int* line1[3]; + int* line2[3]; + int* pv[3]; + QScopedArrayPointer<int> lineBuffer(new int[src->width * 9]); + line1[0] = lineBuffer.data(); + line2[0] = lineBuffer.data() + src->width; + line1[1] = lineBuffer.data() + src->width * 2; + line2[1] = lineBuffer.data() + src->width * 3; + line1[2] = lineBuffer.data() + src->width * 4; + line2[2] = lineBuffer.data() + src->width * 5; + pv[0] = lineBuffer.data() + src->width * 6; + pv[1] = lineBuffer.data() + src->width * 7; + pv[2] = lineBuffer.data() + src->width * 8; + + int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian); + for (int y = 0; y < src->height; y++) { + const uchar* q = src_data; + const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data; + uchar *b = dest_data; + for (int chan = 0; chan < 3; chan++) { + int *l1 = (y&1) ? line2[chan] : line1[chan]; + int *l2 = (y&1) ? line1[chan] : line2[chan]; + if (y == 0) { + for (int i = 0; i < src->width; i++) + l1[i] = q[i*4+chan+endian]; + } + if (y+1 < src->height) { + for (int i = 0; i < src->width; i++) + l2[i] = q2[i*4+chan+endian]; + } + // Bi-directional error diffusion + if (y&1) { + for (int x = 0; x < src->width; x++) { + int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0); + int err = l1[x] - pix * 255 / 5; + pv[chan][x] = pix; + + // Spread the error around... + if (x + 1< src->width) { + l1[x+1] += (err*7)>>4; + l2[x+1] += err>>4; + } + l2[x]+=(err*5)>>4; + if (x>1) + l2[x-1]+=(err*3)>>4; + } + } else { + for (int x = src->width; x-- > 0;) { + int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0); + int err = l1[x] - pix * 255 / 5; + pv[chan][x] = pix; + + // Spread the error around... + if (x > 0) { + l1[x-1] += (err*7)>>4; + l2[x-1] += err>>4; + } + l2[x]+=(err*5)>>4; + if (x + 1 < src->width) + l2[x+1]+=(err*3)>>4; + } + } + } + if (endian) { + for (int x = 0; x < src->width; x++) { + *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]); + } + } else { + for (int x = 0; x < src->width; x++) { + *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]); + } + } + src_data += src->bytes_per_line; + dest_data += dst->bytes_per_line; + } + } else { // OrderedDither + for (int y = 0; y < src->height; y++) { + const QRgb *p = (const QRgb *)src_data; + const QRgb *end = p + src->width; + uchar *b = dest_data; + + int x = 0; + while (p < end) { + uint d = qt_bayer_matrix[y & 15][x & 15] << 8; + +#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16)) + *b++ = + INDEXOF( + DITHER(qRed(*p), d, MAX_R), + DITHER(qGreen(*p), d, MAX_G), + DITHER(qBlue(*p), d, MAX_B) + ); +#undef DITHER + + p++; + x++; + } + src_data += src->bytes_per_line; + dest_data += dst->bytes_per_line; + } + } + + if (src->format != QImage::Format_RGB32 + && src->format != QImage::Format_RGB16) { + const int trans = 216; + Q_ASSERT(dst->colortable.size() > trans); + dst->colortable[trans] = 0; + QScopedPointer<QImageData> mask(QImageData::create(QSize(src->width, src->height), QImage::Format_Mono)); + dither_to_Mono(mask.data(), src, flags, true); + uchar *dst_data = dst->data; + const uchar *mask_data = mask->data; + for (int y = 0; y < src->height; y++) { + for (int x = 0; x < src->width ; x++) { + if (!(mask_data[x>>3] & (0x80 >> (x & 7)))) + dst_data[x] = trans; + } + mask_data += mask->bytes_per_line; + dst_data += dst->bytes_per_line; + } + dst->has_alpha_clut = true; + } + +#undef MAX_R +#undef MAX_G +#undef MAX_B +#undef INDEXOF + + } +} + +static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) +{ + QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); + convert_ARGB_PM_to_ARGB(tmp.data(), src, flags); + convert_RGB_to_Indexed8(dst, tmp.data(), flags); +} + +static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) +{ + convert_RGB_to_Indexed8(dst, src, flags); +} + +static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_Indexed8); + Q_ASSERT(dest->format == QImage::Format_RGB32 + || dest->format == QImage::Format_ARGB32 + || dest->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format); + if (colorTable.size() == 0) { + colorTable.resize(256); + for (int i=0; i<256; ++i) + colorTable[i] = qRgb(i, i, i); + } + + int w = src->width; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + int tableSize = colorTable.size() - 1; + for (int y = 0; y < src->height; y++) { + uint *p = (uint *)dest_data; + const uchar *b = src_data; + uint *end = p + w; + + while (p < end) + *p++ = colorTable.at(qMin<int>(tableSize, *b++)); + + src_data += src->bytes_per_line; + dest_data += dest->bytes_per_line; + } +} + +static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); + Q_ASSERT(dest->format == QImage::Format_RGB32 + || dest->format == QImage::Format_ARGB32 + || dest->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + QVector<QRgb> colorTable = fix_color_table(src->colortable, dest->format); + + // Default to black / white colors + if (colorTable.size() < 2) { + if (colorTable.size() == 0) + colorTable << 0xff000000; + colorTable << 0xffffffff; + } + + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + if (src->format == QImage::Format_Mono) { + for (int y = 0; y < dest->height; y++) { + uint *p = (uint *)dest_data; + for (int x = 0; x < dest->width; x++) + *p++ = colorTable.at((src_data[x>>3] >> (7 - (x & 7))) & 1); + + src_data += src->bytes_per_line; + dest_data += dest->bytes_per_line; + } + } else { + for (int y = 0; y < dest->height; y++) { + uint *p = (uint *)dest_data; + for (int x = 0; x < dest->width; x++) + *p++ = colorTable.at((src_data[x>>3] >> (x & 7)) & 1); + + src_data += src->bytes_per_line; + dest_data += dest->bytes_per_line; + } + } +} + + +static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB); + Q_ASSERT(dest->format == QImage::Format_Indexed8); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + QVector<QRgb> ctbl = src->colortable; + if (ctbl.size() > 2) { + ctbl.resize(2); + } else if (ctbl.size() < 2) { + if (ctbl.size() == 0) + ctbl << 0xff000000; + ctbl << 0xffffffff; + } + dest->colortable = ctbl; + dest->has_alpha_clut = src->has_alpha_clut; + + + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + if (src->format == QImage::Format_Mono) { + for (int y = 0; y < dest->height; y++) { + uchar *p = dest_data; + for (int x = 0; x < dest->width; x++) + *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1; + src_data += src->bytes_per_line; + dest_data += dest->bytes_per_line; + } + } else { + for (int y = 0; y < dest->height; y++) { + uchar *p = dest_data; + for (int x = 0; x < dest->width; x++) + *p++ = (src_data[x>>3] >> (x & 7)) & 1; + src_data += src->bytes_per_line; + dest_data += dest->bytes_per_line; + } + } +} + +// first index source, second dest +Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = +{ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, + 0, + swap_bit_order, + convert_Mono_to_Indexed8, + convert_Mono_to_X32, + convert_Mono_to_X32, + convert_Mono_to_X32, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_Mono + + { + 0, + swap_bit_order, + 0, + convert_Mono_to_Indexed8, + convert_Mono_to_X32, + convert_Mono_to_X32, + convert_Mono_to_X32, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_MonoLSB + + { + 0, + convert_X_to_Mono, + convert_X_to_Mono, + 0, + convert_Indexed8_to_X32, + convert_Indexed8_to_X32, + convert_Indexed8_to_X32, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_Indexed8 + + { + 0, + convert_X_to_Mono, + convert_X_to_Mono, + convert_RGB_to_Indexed8, + 0, + mask_alpha_converter, + mask_alpha_converter, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGB_to_RGBA, + convert_RGB_to_RGBA, + convert_RGB_to_RGBA + }, // Format_RGB32 + + { + 0, + convert_X_to_Mono, + convert_X_to_Mono, + convert_ARGB_to_Indexed8, + mask_alpha_converter, + 0, + convert_ARGB_to_ARGB_PM, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_ARGB_to_RGBx, + convert_ARGB_to_RGBA, + convert_ARGB_to_RGBA_PM, + }, // Format_ARGB32 + + { + 0, + convert_ARGB_PM_to_Mono, + convert_ARGB_PM_to_Mono, + convert_ARGB_PM_to_Indexed8, + convert_ARGB_PM_to_RGB, + convert_ARGB_PM_to_ARGB, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_ARGB_PM_to_RGBx, + convert_ARGB_PM_to_RGBA, + convert_ARGB_to_RGBA, + }, // Format_ARGB32_Premultiplied + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_RGB16 + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_ARGB8565_Premultiplied + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_RGB666 + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_ARGB6666_Premultiplied + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_RGB555 + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_ARGB8555_Premultiplied + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_RGB888 + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_RGB444 + + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + }, // Format_ARGB4444_Premultiplied + { + 0, + 0, + 0, + 0, + convert_RGBA_to_RGB, + convert_RGBA_to_ARGB, + convert_RGBA_to_ARGB_PM, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + mask_alpha_converter_RGBx, + mask_alpha_converter_RGBx, + }, // Format_RGBX8888 + { + 0, + 0, + 0, + 0, + convert_RGBA_to_RGB, + convert_RGBA_to_ARGB, + convert_RGBA_to_ARGB_PM, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + mask_alpha_converter_RGBx, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + 0, + convert_ARGB_to_ARGB_PM, +#else + 0, + 0 +#endif + }, // Format_RGBA8888 + + { + 0, + 0, + 0, + 0, + convert_RGBA_PM_to_RGB, + convert_RGBA_PM_to_ARGB, + convert_RGBA_to_ARGB, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + convert_ARGB_PM_to_RGB, + convert_ARGB_PM_to_ARGB, + 0, +#else + 0, + 0, + 0 +#endif + } // Format_RGBA8888_Premultiplied +}; + +InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = +{ + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_Mono + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_MonoLSB + { + 0, + 0, + 0, + 0, + 0, + convert_indexed8_to_RGB_inplace, + convert_indexed8_to_ARGB_PM_inplace, + convert_indexed8_to_RGB16_inplace, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, // Format_Indexed8 + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGB_to_RGB16_inplace, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, // Format_RGB32 + { + 0, + 0, + 0, + 0, + 0, + 0, +#ifdef __SSE2__ + convert_ARGB_to_ARGB_PM_inplace_sse2, +#else + convert_ARGB_to_ARGB_PM_inplace, +#endif + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_ARGB_to_RGBA_inplace, + 0, + }, // Format_ARGB32 + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_ARGB_to_RGBA_inplace + }, // Format_ARGB32_Premultiplied + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_RGB16 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_ARGB8565_Premultiplied + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_RGB666 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_ARGB6666_Premultiplied + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_RGB555 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_ARGB8555_Premultiplied + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_RGB888 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_RGB444 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_ARGB4444_Premultiplied + { + 0, + 0, + 0, + 0, + 0, + convert_RGBA_to_ARGB_inplace, + convert_RGBA_to_ARGB_inplace, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, // Format_RGBX8888 + { + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA_to_ARGB_inplace, + convert_RGBA_to_ARGB_PM_inplace, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, // Format_RGBA8888 + { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA_to_ARGB_inplace, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + } // Format_RGBA8888_Premultiplied +}; + +void qInitImageConversions() +{ +#if defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSSE3) + if (qCpuHasFeature(SSSE3)) { + extern void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); + qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3; + qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3; + qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3; + } +#endif + +#ifdef __ARM_NEON__ + extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); + qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon; + qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_neon; + qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_neon; +#endif + +#ifdef QT_COMPILER_SUPPORTS_MIPS_DSPR2 + extern bool convert_ARGB_to_ARGB_PM_inplace_mips_dspr2(QImageData *data, Qt::ImageConversionFlags); + inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_mips_dspr2; + return; +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/image/qimage_neon.cpp b/src/gui/image/qimage_neon.cpp index 1ac0a87272..60c2da6a58 100644 --- a/src/gui/image/qimage_neon.cpp +++ b/src/gui/image/qimage_neon.cpp @@ -43,7 +43,7 @@ #include <private/qimage_p.h> #include <private/qsimd_p.h> -#ifdef QT_COMPILER_SUPPORTS_NEON +#ifdef __ARM_NEON__ QT_BEGIN_NAMESPACE @@ -111,4 +111,4 @@ void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::I QT_END_NAMESPACE -#endif // QT_COMPILER_SUPPORTS_NEON +#endif // __ARM_NEON__ diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 36f117df60..81730b92f2 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -108,7 +108,20 @@ struct Q_GUI_EXPORT QImageData { // internal image data QPaintEngine *paintEngine; }; +typedef void (*Image_Converter)(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); +typedef bool (*InPlace_Image_Converter)(QImageData *data, Qt::ImageConversionFlags); + +extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats]; +extern InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats]; + +void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); +bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); + +void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha); + void qInitImageConversions(); + +const uchar *qt_get_bitflip_array(); Q_GUI_EXPORT void qGamma_correct_back_to_linear_cs(QImage *image); inline int qt_depthForFormat(QImage::Format format) diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 636d86991b..091837b8b4 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -190,37 +190,36 @@ enum _qt_BuiltInFormatType { struct _qt_BuiltInFormatStruct { - _qt_BuiltInFormatType type; const char *extension; const char *mimeType; }; static const _qt_BuiltInFormatStruct _qt_BuiltInFormats[] = { #ifndef QT_NO_IMAGEFORMAT_PNG - {_qt_PngFormat, "png", "image/png"}, + {"png", "image/png"}, #endif #ifndef QT_NO_IMAGEFORMAT_JPEG - {_qt_JpgFormat, "jpg", "image/jpeg"}, - {_qt_JpegFormat, "jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, #endif #ifdef QT_BUILTIN_GIF_READER - {_qt_GifFormat, "gif", "image/gif"}, + {"gif", "image/gif"}, #endif #ifndef QT_NO_IMAGEFORMAT_BMP - {_qt_BmpFormat, "bmp", "image/bmp"}, + {"bmp", "image/bmp"}, #endif #ifndef QT_NO_IMAGEFORMAT_PPM - {_qt_PpmFormat, "ppm", "image/x-portable-pixmap"}, - {_qt_PgmFormat, "pgm", "image/x-portable-graymap"}, - {_qt_PbmFormat, "pbm", "image/x-portable-bitmap"}, + {"ppm", "image/x-portable-pixmap"}, + {"pgm", "image/x-portable-graymap"}, + {"pbm", "image/x-portable-bitmap"}, #endif #ifndef QT_NO_IMAGEFORMAT_XBM - {_qt_XbmFormat, "xbm", "image/x-xbitmap"}, + {"xbm", "image/x-xbitmap"}, #endif #ifndef QT_NO_IMAGEFORMAT_XPM - {_qt_XpmFormat, "xpm", "image/x-xpixmap"}, + {"xpm", "image/x-xpixmap"}, #endif - {_qt_NoFormat, "", ""} + {"", ""} }; static QImageIOHandler *createReadHandlerHelper(QIODevice *device, @@ -423,10 +422,8 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, QByteArray subType; int numFormats = _qt_NumFormats; while (device && numFormats >= 0) { - const _qt_BuiltInFormatStruct *formatStruct = &_qt_BuiltInFormats[currentFormat]; - const qint64 pos = device->pos(); - switch (formatStruct->type) { + switch (currentFormat) { #ifndef QT_NO_IMAGEFORMAT_PNG case _qt_PngFormat: if (QPngHandler::canRead(device)) @@ -482,7 +479,7 @@ static QImageIOHandler *createReadHandlerHelper(QIODevice *device, if (handler) { #ifdef QIMAGEREADER_DEBUG - qDebug() << "QImageReader::createReadHandler: the" << formatStruct->extension + qDebug() << "QImageReader::createReadHandler: the" << _qt_BuiltInFormats[currentFormat].extension << "built-in handler can read this data"; #endif break; diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp index 3f90bb42f0..bd358b7228 100644 --- a/src/gui/image/qjpeghandler.cpp +++ b/src/gui/image/qjpeghandler.cpp @@ -861,24 +861,18 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_avx(quint32 *dst, const QJpegHandler::QJpegHandler() : d(new QJpegHandlerPrivate(this)) { -#if defined(QT_COMPILER_SUPPORTS_NEON) +#if defined(__ARM_NEON__) // from qimage_neon.cpp if (qCpuHasFeature(NEON)) rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon; -#endif // QT_COMPILER_SUPPORTS_NEON +#endif // __ARM_NEON__ #if defined(QT_COMPILER_SUPPORTS_SSSE3) // from qimage_ssse3.cpp if (false) { -# if defined(QT_COMPILER_SUPPORTS_AVX) - } else if (qCpuHasFeature(AVX)) { - rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_avx; -# endif -# ifndef __AVX__ } else if (qCpuHasFeature(SSSE3)) { rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_ssse3; -# endif } #endif // QT_COMPILER_SUPPORTS_SSSE3 } diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 6dced54d20..86c4dfbdca 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -1617,6 +1617,28 @@ QPixmap QPixmap::fromImage(const QImage &image, Qt::ImageConversionFlags flags) } /*! + \fn QPixmap QPixmap::fromImage(QImage &&image, Qt::ImageConversionFlags flags) + \since 5.3 + \overload + + Converts the given \a image to a pixmap without copying if possible. +*/ + + +/*! + \internal +*/ +QPixmap QPixmap::fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags) +{ + if (image.isNull()) + return QPixmap(); + + QScopedPointer<QPlatformPixmap> data(QGuiApplicationPrivate::platformIntegration()->createPlatformPixmap(QPlatformPixmap::PixmapType)); + data->fromImageInPlace(image, flags); + return QPixmap(data.take()); +} + +/*! \fn QPixmap QPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags) Create a QPixmap from an image read directly from an \a imageReader. diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index f1fce03c80..0efd606283 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -131,6 +131,12 @@ public: QImage toImage() const; static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor); static QPixmap fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags = Qt::AutoColor); +#ifdef Q_COMPILER_RVALUE_REFS + static QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags = Qt::AutoColor) + { + return fromImageInPlace(image, flags); + } +#endif bool load(const QString& fileName, const char *format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor); bool loadFromData(const uchar *buf, uint len, const char* format = 0, Qt::ImageConversionFlags flags = Qt::AutoColor); @@ -167,6 +173,7 @@ public: protected: int metric(PaintDeviceMetric) const; + static QPixmap fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor); private: QExplicitlySharedDataPointer<QPlatformPixmap> data; diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index f9a017c281..37259adcd2 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -135,11 +135,16 @@ bool QRasterPlatformPixmap::fromData(const uchar *buffer, uint len, const char * void QRasterPlatformPixmap::fromImage(const QImage &sourceImage, Qt::ImageConversionFlags flags) { - Q_UNUSED(flags); QImage image = sourceImage; createPixmapForImage(image, flags, /* inplace = */false); } +void QRasterPlatformPixmap::fromImageInPlace(QImage &sourceImage, + Qt::ImageConversionFlags flags) +{ + createPixmapForImage(sourceImage, flags, /* inplace = */true); +} + void QRasterPlatformPixmap::fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags) { @@ -182,7 +187,7 @@ void QRasterPlatformPixmap::fill(const QColor &color) if (alpha != 255) { if (!image.hasAlphaChannel()) { QImage::Format toFormat; -#if !(defined(QT_COMPILER_SUPPORTS_NEON) || defined(__SSE2__)) +#if !(defined(__ARM_NEON__) || defined(__SSE2__)) if (image.format() == QImage::Format_RGB16) toFormat = QImage::Format_ARGB8565_Premultiplied; else if (image.format() == QImage::Format_RGB666) @@ -311,7 +316,7 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC QImage::Format opaqueFormat = QNativeImage::systemFormat(); QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; -#if !defined(QT_COMPILER_SUPPORTS_NEON) && !defined(__SSE2__) +#if !defined(__ARM_NEON__) && !defined(__SSE2__) switch (opaqueFormat) { case QImage::Format_RGB16: alphaFormat = QImage::Format_ARGB8565_Premultiplied; diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index cef8821888..b273d65c9f 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -69,6 +69,7 @@ public: void resize(int width, int height); bool fromData(const uchar *buffer, uint len, const char *format, Qt::ImageConversionFlags flags); void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void fromImageInPlace(QImage &image, Qt::ImageConversionFlags flags); void fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags); void copy(const QPlatformPixmap *data, const QRect &rect); diff --git a/src/gui/image/qplatformpixmap.h b/src/gui/image/qplatformpixmap.h index 08e03f10bd..435811eb84 100644 --- a/src/gui/image/qplatformpixmap.h +++ b/src/gui/image/qplatformpixmap.h @@ -69,7 +69,8 @@ public: }; enum ClassId { RasterClass, DirectFBClass, - BlitterClass, CustomClass = 1024 }; + BlitterClass, Direct2DClass, + CustomClass = 1024 }; QPlatformPixmap(PixelType pixelType, int classId); virtual ~QPlatformPixmap(); @@ -79,6 +80,12 @@ public: virtual void resize(int width, int height) = 0; virtual void fromImage(const QImage &image, Qt::ImageConversionFlags flags) = 0; + virtual void fromImageInPlace(QImage &image, + Qt::ImageConversionFlags flags) + { + fromImage(image, flags); + } + virtual void fromImageReader(QImageReader *imageReader, Qt::ImageConversionFlags flags); diff --git a/src/gui/image/qpnghandler.pri b/src/gui/image/qpnghandler.pri index bedf23ff12..aca7e2c568 100644 --- a/src/gui/image/qpnghandler.pri +++ b/src/gui/image/qpnghandler.pri @@ -2,7 +2,7 @@ INCLUDEPATH *= $$PWD HEADERS += $$PWD/qpnghandler_p.h SOURCES += $$PWD/qpnghandler.cpp contains(QT_CONFIG, system-png) { - if(unix|win32-g++*): LIBS_PRIVATE += -lpng + if(unix|mingw): LIBS_PRIVATE += -lpng else:win32: LIBS += libpng.lib } else { diff --git a/src/gui/image/qxbmhandler.cpp b/src/gui/image/qxbmhandler.cpp index aceb6623ea..5311afd745 100644 --- a/src/gui/image/qxbmhandler.cpp +++ b/src/gui/image/qxbmhandler.cpp @@ -103,7 +103,7 @@ static bool read_xbm_header(QIODevice *device, int& w, int& h) // "#define .._height <num>" readBytes = device->readLine(buf, buflen); if (readBytes <= 0) - return false; + return false; buf[readBytes - 1] = '\0'; sbuf = QString::fromLatin1(buf); @@ -183,9 +183,9 @@ static bool read_xbm_image(QIODevice *device, QImage *outImage) static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const QString &fileName) { QImage image = sourceImage; - int w = image.width(); - int h = image.height(); - int i; + int w = image.width(); + int h = image.height(); + int i; QString s = fileName; // get file base name int msize = s.length() + 100; char *buf = new char[msize]; @@ -203,16 +203,16 @@ static bool write_xbm_image(const QImage &sourceImage, QIODevice *device, const bool invert = qGray(image.color(0)) < qGray(image.color(1)); char hexrep[16]; for (i=0; i<10; i++) - hexrep[i] = '0' + i; + hexrep[i] = '0' + i; for (i=10; i<16; i++) - hexrep[i] = 'a' -10 + i; + hexrep[i] = 'a' -10 + i; if (invert) { - char t; - for (i=0; i<8; i++) { - t = hexrep[15-i]; - hexrep[15-i] = hexrep[i]; - hexrep[i] = t; - } + char t; + for (i=0; i<8; i++) { + t = hexrep[15-i]; + hexrep[15-i] = hexrep[i]; + hexrep[i] = t; + } } int bcnt = 0; char *p = buf; diff --git a/src/gui/kernel/qdrag.cpp b/src/gui/kernel/qdrag.cpp index 91aae94730..465c04cdc8 100644 --- a/src/gui/kernel/qdrag.cpp +++ b/src/gui/kernel/qdrag.cpp @@ -103,7 +103,7 @@ QT_BEGIN_NAMESPACE \sa {Drag and Drop}, QClipboard, QMimeData, QWindowsMime, QMacPasteboardMime, {Draggable Icons Example}, {Draggable Text Example}, {Drop Site Example}, - {Fridge Magnets Example} + {Fridge Magnets Example} */ /*! @@ -253,8 +253,8 @@ Qt::DropAction QDrag::exec(Qt::DropActions supportedActions) loop. Other events are still delivered to the application while the operation is performed. On Windows, the Qt event loop is blocked during the operation. However, QDrag::exec() on - Windows causes processEvents() to be called frequently to keep the GUI responsive. - If any loops or operations are called while a drag operation is active, it will block the drag operation. + Windows causes processEvents() to be called frequently to keep the GUI responsive. + If any loops or operations are called while a drag operation is active, it will block the drag operation. */ Qt::DropAction QDrag::exec(Qt::DropActions supportedActions, Qt::DropAction defaultDropAction) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 131f1863a5..a474d70190 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -274,6 +274,24 @@ QMouseEvent::~QMouseEvent() { } +/*! + \since 5.3 + + Returns information about the mouse event source. + + The mouse event source can be used to distinguish between genuine + and artificial mouse events. The latter are events that are + synthesized from touch events by the operating system or Qt itself. + + \note Many platforms provide no such information. On such platforms + \l Qt::MouseEventNotSynthesized is returned always. + + \sa Qt::MouseEventSource + */ +Qt::MouseEventSource QMouseEvent::source() const +{ + return QGuiApplicationPrivate::mouseEventSource(this); +} /*! \fn QPointF QMouseEvent::localPos() const diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index d22e423248..b6b1e0c76b 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -135,6 +135,8 @@ public: QT_DEPRECATED inline QPointF posF() const { return l; } #endif + Qt::MouseEventSource source() const; + protected: QPointF l, w, s; Qt::MouseButton b; diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 0711a37edd..a753e6018c 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -59,6 +59,7 @@ #include <QtCore/qmutex.h> #include <QtCore/private/qthread_p.h> #include <QtCore/qdir.h> +#include <QtCore/qlibraryinfo.h> #include <QtDebug> #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" @@ -887,11 +888,14 @@ QString QGuiApplication::platformName() *QGuiApplicationPrivate::platform_name : QString(); } -static void init_platform(const QString &pluginArgument, const QString &platformPluginPath, int &argc, char **argv) +static void init_platform(const QString &pluginArgument, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv) { // Split into platform name and arguments QStringList arguments = pluginArgument.split(QLatin1Char(':')); const QString name = arguments.takeFirst().toLower(); + QString argumentsKey = name; + argumentsKey[0] = argumentsKey.at(0).toUpper(); + arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey)); // Create the platform integration. QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); @@ -918,15 +922,21 @@ static void init_platform(const QString &pluginArgument, const QString &platform } // Create the platform theme: - // 1) Ask the platform integration for a list of names. - const QStringList themeNames = QGuiApplicationPrivate::platform_integration->themeNames(); + + // 1) Fetch the platform name from the environment if present. + QStringList themeNames; + if (!platformThemeName.isEmpty()) + themeNames.append(platformThemeName); + + // 2) Ask the platform integration for a list of names and try loading them. + themeNames += QGuiApplicationPrivate::platform_integration->themeNames(); foreach (const QString &themeName, themeNames) { QGuiApplicationPrivate::platform_theme = QPlatformThemeFactory::create(themeName, platformPluginPath); if (QGuiApplicationPrivate::platform_theme) break; } - // 2) If none found, look for a theme plugin. Theme plugins are located in the + // 3) If none found, look for a theme plugin. Theme plugins are located in the // same directory as platform plugins. if (!QGuiApplicationPrivate::platform_theme) { foreach (const QString &themeName, themeNames) { @@ -937,7 +947,7 @@ static void init_platform(const QString &pluginArgument, const QString &platform // No error message; not having a theme plugin is allowed. } - // 3) Fall back on the built-in "null" platform theme. + // 4) Fall back on the built-in "null" platform theme. if (!QGuiApplicationPrivate::platform_theme) QGuiApplicationPrivate::platform_theme = new QPlatformTheme; @@ -997,6 +1007,8 @@ void QGuiApplicationPrivate::createPlatformIntegration() platformName = platformNameEnv; } + QString platformThemeName = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORMTHEME")); + // Get command line params int j = argc ? 1 : 0; @@ -1012,6 +1024,9 @@ void QGuiApplicationPrivate::createPlatformIntegration() } else if (arg == "-platform") { if (++i < argc) platformName = argv[i]; + } else if (arg == "-platformtheme") { + if (++i < argc) + platformThemeName = QString::fromLocal8Bit(argv[i]); } else if (arg == "-qwindowgeometry" || (platformName == "xcb" && arg == "-geometry")) { if (++i < argc) windowGeometrySpecification = QWindowGeometrySpecification::fromArgument(argv[i]); @@ -1025,7 +1040,7 @@ void QGuiApplicationPrivate::createPlatformIntegration() argc = j; } - init_platform(QLatin1String(platformName), platformPluginPath, argc, argv); + init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); } @@ -1571,6 +1586,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers); ev.setTimestamp(e->timestamp); + setMouseEventSource(&ev, e->source); #ifndef QT_NO_CURSOR if (!e->synthetic) { if (const QScreen *screen = window->screen()) @@ -1625,6 +1641,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QMouseEvent dblClickEvent(doubleClickType, localPoint, localPoint, globalPoint, button, buttons, e->modifiers); dblClickEvent.setTimestamp(e->timestamp); + setMouseEventSource(&dblClickEvent, e->source); QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent); } } @@ -2041,7 +2058,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To synthIt->pos, synthIt->screenPos, Qt::NoButton, - e->modifiers); + e->modifiers, + Qt::MouseEventSynthesizedByQt); fake.synthetic = true; processMouseEvent(&fake); } @@ -3146,9 +3164,16 @@ void QGuiApplicationPrivate::_q_updateFocusObject(QObject *object) emit q->focusObjectChanged(object); } +enum { + MouseCapsMask = 0xFF, + MouseSourceMaskDst = 0xFF00, + MouseSourceMaskSrc = MouseCapsMask, + MouseSourceShift = 8, +}; + int QGuiApplicationPrivate::mouseEventCaps(QMouseEvent *event) { - return event->caps; + return event->caps & MouseCapsMask; } QVector2D QGuiApplicationPrivate::mouseEventVelocity(QMouseEvent *event) @@ -3158,16 +3183,26 @@ QVector2D QGuiApplicationPrivate::mouseEventVelocity(QMouseEvent *event) void QGuiApplicationPrivate::setMouseEventCapsAndVelocity(QMouseEvent *event, int caps, const QVector2D &velocity) { - event->caps = caps; + Q_ASSERT(caps <= MouseCapsMask); + event->caps &= ~MouseCapsMask; + event->caps |= caps & MouseCapsMask; event->velocity = velocity; } -void QGuiApplicationPrivate::setMouseEventCapsAndVelocity(QMouseEvent *event, QMouseEvent *other) +Qt::MouseEventSource QGuiApplicationPrivate::mouseEventSource(const QMouseEvent *event) { - event->caps = other->caps; - event->velocity = other->velocity; + return Qt::MouseEventSource((event->caps & MouseSourceMaskDst) >> MouseSourceShift); } +void QGuiApplicationPrivate::setMouseEventSource(QMouseEvent *event, Qt::MouseEventSource source) +{ + // Mouse event synthesization status is encoded in the caps field because + // QTouchDevice::CapabilityFlag uses only 6 bits from it. + int value = source; + Q_ASSERT(value <= MouseSourceMaskSrc); + event->caps &= ~MouseSourceMaskDst; + event->caps |= (value & MouseSourceMaskSrc) << MouseSourceShift; +} #include "moc_qguiapplication.cpp" diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index 218036033e..95ee8eb295 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -171,7 +171,7 @@ public: { if (!(alignment & Qt::AlignHorizontal_Mask)) alignment |= Qt::AlignLeft; - if ((alignment & Qt::AlignAbsolute) == 0 && (alignment & (Qt::AlignLeft | Qt::AlignRight))) { + if (!(alignment & Qt::AlignAbsolute) && (alignment & (Qt::AlignLeft | Qt::AlignRight))) { if (direction == Qt::RightToLeft) alignment ^= (Qt::AlignLeft | Qt::AlignRight); alignment |= Qt::AlignAbsolute; @@ -268,7 +268,9 @@ public: static int mouseEventCaps(QMouseEvent *event); static QVector2D mouseEventVelocity(QMouseEvent *event); static void setMouseEventCapsAndVelocity(QMouseEvent *event, int caps, const QVector2D &velocity); - static void setMouseEventCapsAndVelocity(QMouseEvent *event, QMouseEvent *other); + + static Qt::MouseEventSource mouseEventSource(const QMouseEvent *event); + static void setMouseEventSource(QMouseEvent *event, Qt::MouseEventSource source); const QDrawHelperGammaTables *gammaTables(); diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp index b4e936f818..1739e8c6fd 100644 --- a/src/gui/kernel/qguivariant.cpp +++ b/src/gui/kernel/qguivariant.cpp @@ -165,7 +165,7 @@ public: #ifndef QT_NO_ICON bool delegate(const QIcon *) { - return false; + return v_cast<QIcon>(Base::m_a)->cacheKey() == v_cast<QIcon>(Base::m_b)->cacheKey(); } #endif bool delegate(const void *p) { return Base::delegate(p); } diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 5770b76f1f..4569457a20 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -1567,7 +1567,7 @@ QDataStream &operator<<(QDataStream &s, const QKeySequence &keysequence) */ QDataStream &operator>>(QDataStream &s, QKeySequence &keysequence) { - qAtomicDetach(keysequence.d); + qAtomicDetach(keysequence.d); QList<quint32> list; s >> list; for (int i = 0; i < 4; ++i) diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 26aaf931b3..bec201f3f7 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -359,6 +359,8 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const return true; case SetFocusOnTouchRelease: return QVariant(false); + case MousePressAndHoldInterval: + return QPlatformTheme::defaultThemeHint(QPlatformTheme::MousePressAndHoldInterval); } return 0; diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index 580fc15233..e3676b1be8 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -148,7 +148,8 @@ public: SynthesizeMouseFromTouchEvents, PasswordMaskCharacter, SetFocusOnTouchRelease, - ShowIsMaximized + ShowIsMaximized, + MousePressAndHoldInterval }; virtual QVariant styleHint(StyleHint hint) const; diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index 3548ec0199..90edf4c49f 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -51,6 +51,7 @@ #include <private/qiconloader_p.h> #include <private/qguiapplication_p.h> #include <qpa/qplatformintegration.h> +#include <qpa/qplatformdialoghelper.h> QT_BEGIN_NAMESPACE @@ -79,6 +80,9 @@ QT_BEGIN_NAMESPACE \value MouseDoubleClickInterval (int) Mouse double click interval in ms, overriding QPlatformIntegration::styleHint. + \value MousePressAndHoldInterval (int) Mouse press and hold interval in ms, + overriding QPlatformIntegration::styleHint. + \value StartDragDistance (int) Start drag distance, overriding QPlatformIntegration::styleHint. @@ -425,6 +429,8 @@ QVariant QPlatformTheme::themeHint(ThemeHint hint) const return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::PasswordMaskDelay); case QPlatformTheme::PasswordMaskCharacter: return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::PasswordMaskCharacter); + case QPlatformTheme::MousePressAndHoldInterval: + return QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::MousePressAndHoldInterval); default: return QPlatformTheme::defaultThemeHint(hint); } @@ -491,6 +497,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) case DialogSnapToDefaultButton: case ContextMenuOnMouseRelease: return QVariant(false); + case MousePressAndHoldInterval: + return QVariant(800); } return QVariant(); } @@ -620,6 +628,63 @@ QList<QKeySequence> QPlatformTheme::keyBindings(QKeySequence::StandardKey key) c return list; } +/*! + Returns the text of a standard \a button. + + \since 5.3 + \sa QMessageDialogOptions::StandardButton + */ + +QString QPlatformTheme::standardButtonText(int button) const +{ + return QPlatformTheme::defaultStandardButtonText(button); +} + +QString QPlatformTheme::defaultStandardButtonText(int button) +{ + switch (button) { + case QMessageDialogOptions::Ok: + return QCoreApplication::translate("QPlatformTheme", "OK"); + case QMessageDialogOptions::Save: + return QCoreApplication::translate("QPlatformTheme", "Save"); + case QMessageDialogOptions::SaveAll: + return QCoreApplication::translate("QPlatformTheme", "Save All"); + case QMessageDialogOptions::Open: + return QCoreApplication::translate("QPlatformTheme", "Open"); + case QMessageDialogOptions::Yes: + return QCoreApplication::translate("QPlatformTheme", "&Yes"); + case QMessageDialogOptions::YesToAll: + return QCoreApplication::translate("QPlatformTheme", "Yes to &All"); + case QMessageDialogOptions::No: + return QCoreApplication::translate("QPlatformTheme", "&No"); + case QMessageDialogOptions::NoToAll: + return QCoreApplication::translate("QPlatformTheme", "N&o to All"); + case QMessageDialogOptions::Abort: + return QCoreApplication::translate("QPlatformTheme", "Abort"); + case QMessageDialogOptions::Retry: + return QCoreApplication::translate("QPlatformTheme", "Retry"); + case QMessageDialogOptions::Ignore: + return QCoreApplication::translate("QPlatformTheme", "Ignore"); + case QMessageDialogOptions::Close: + return QCoreApplication::translate("QPlatformTheme", "Close"); + case QMessageDialogOptions::Cancel: + return QCoreApplication::translate("QPlatformTheme", "Cancel"); + case QMessageDialogOptions::Discard: + return QCoreApplication::translate("QPlatformTheme", "Discard"); + case QMessageDialogOptions::Help: + return QCoreApplication::translate("QPlatformTheme", "Help"); + case QMessageDialogOptions::Apply: + return QCoreApplication::translate("QPlatformTheme", "Apply"); + case QMessageDialogOptions::Reset: + return QCoreApplication::translate("QPlatformTheme", "Reset"); + case QMessageDialogOptions::RestoreDefaults: + return QCoreApplication::translate("QPlatformTheme", "Restore Defaults"); + default: + break; + } + return QString(); +} + unsigned QPlatformThemePrivate::currentKeyPlatforms() { const uint keyboardScheme = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::KeyboardScheme).toInt(); diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index 5cdec48ca3..205a5bab69 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -107,7 +107,8 @@ public: IconPixmapSizes, PasswordMaskCharacter, DialogSnapToDefaultButton, - ContextMenuOnMouseRelease + ContextMenuOnMouseRelease, + MousePressAndHoldInterval }; enum DialogType { @@ -295,7 +296,10 @@ public: virtual QList<QKeySequence> keyBindings(QKeySequence::StandardKey key) const; + virtual QString standardButtonText(int button) const; + static QVariant defaultThemeHint(ThemeHint hint); + static QString defaultStandardButtonText(int button); protected: explicit QPlatformTheme(QPlatformThemePrivate *priv); diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp index 954d47f18c..fe29627c5a 100644 --- a/src/gui/kernel/qplatformwindow.cpp +++ b/src/gui/kernel/qplatformwindow.cpp @@ -521,14 +521,13 @@ QRect QPlatformWindow::initialGeometry(const QWindow *w, const QRect &initialGeometry, int defaultWidth, int defaultHeight) { QRect rect(initialGeometry); - if (rect.isNull()) { - QSize minimumSize = w->minimumSize(); - if (minimumSize.width() > 0 || minimumSize.height() > 0) { - rect.setSize(minimumSize); - } else { - rect.setWidth(defaultWidth); - rect.setHeight(defaultHeight); - } + if (rect.width() == 0) { + const int minWidth = w->minimumWidth(); + rect.setWidth(minWidth > 0 ? minWidth : defaultWidth); + } + if (rect.height() == 0) { + const int minHeight = w->minimumHeight(); + rect.setHeight(minHeight > 0 ? minHeight : defaultHeight); } if (w->isTopLevel() && qt_window_private(const_cast<QWindow*>(w))->positionAutomatic && w->type() != Qt::Popup) { diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 513e21937e..c915ed3928 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -359,7 +359,7 @@ bool QShortcutMap::tryShortcutEvent(QObject *o, QKeyEvent *e) resetState(); dispatchEvent(e); default: - break; + break; } // If nextState is QKeySequence::ExactMatch && identicals.count == 0 // we've only found disabled shortcuts diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index 68eb724454..e1468942af 100644 --- a/src/gui/kernel/qstylehints.cpp +++ b/src/gui/kernel/qstylehints.cpp @@ -62,6 +62,25 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th, return QGuiApplicationPrivate::platformIntegration()->styleHint(ih); } +class QStyleHintsPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QStyleHints) +public: + inline QStyleHintsPrivate() + : m_mouseDoubleClickInterval(-1) + , m_startDragDistance(-1) + , m_startDragTime(-1) + , m_keyboardInputInterval(-1) + , m_cursorFlashTime(-1) + {} + + int m_mouseDoubleClickInterval; + int m_startDragDistance; + int m_startDragTime; + int m_keyboardInputInterval; + int m_cursorFlashTime; +}; + /*! \class QStyleHints \since 5.0 @@ -80,17 +99,55 @@ static inline QVariant themeableHint(QPlatformTheme::ThemeHint th, \sa QGuiApplication::styleHints(), QPlatformTheme */ QStyleHints::QStyleHints() - : QObject() + : QObject(*new QStyleHintsPrivate(), 0) { } /*! + Sets the \a mouseDoubleClickInterval. + \internal + \sa mouseDoubleClickInterval() + \since 5.3 +*/ +void QStyleHints::setMouseDoubleClickInterval(int mouseDoubleClickInterval) +{ + Q_D(QStyleHints); + d->m_mouseDoubleClickInterval = mouseDoubleClickInterval; +} + +/*! Returns the time limit in milliseconds that distinguishes a double click from two consecutive mouse clicks. */ int QStyleHints::mouseDoubleClickInterval() const { - return themeableHint(QPlatformTheme::MouseDoubleClickInterval, QPlatformIntegration::MouseDoubleClickInterval).toInt(); + Q_D(const QStyleHints); + return d->m_mouseDoubleClickInterval >= 0 ? + d->m_mouseDoubleClickInterval : + themeableHint(QPlatformTheme::MouseDoubleClickInterval, QPlatformIntegration::MouseDoubleClickInterval).toInt(); +} + +/*! + Returns the time limit in milliseconds that activates + a press and hold. + + \since 5.3 +*/ +int QStyleHints::mousePressAndHoldInterval() const +{ + return themeableHint(QPlatformTheme::MousePressAndHoldInterval, QPlatformIntegration::MousePressAndHoldInterval).toInt(); +} + +/*! + Sets the \a startDragDistance. + \internal + \sa startDragDistance() + \since 5.3 +*/ +void QStyleHints::setStartDragDistance(int startDragDistance) +{ + Q_D(QStyleHints); + d->m_startDragDistance = startDragDistance; } /*! @@ -112,7 +169,22 @@ int QStyleHints::mouseDoubleClickInterval() const */ int QStyleHints::startDragDistance() const { - return themeableHint(QPlatformTheme::StartDragDistance, QPlatformIntegration::StartDragDistance).toInt(); + Q_D(const QStyleHints); + return d->m_startDragDistance >= 0 ? + d->m_startDragDistance : + themeableHint(QPlatformTheme::StartDragDistance, QPlatformIntegration::StartDragDistance).toInt(); +} + +/*! + Sets the \a startDragDragTime. + \internal + \sa startDragTime() + \since 5.3 +*/ +void QStyleHints::setStartDragTime(int startDragTime) +{ + Q_D(QStyleHints); + d->m_startDragTime = startDragTime; } /*! @@ -127,7 +199,10 @@ int QStyleHints::startDragDistance() const */ int QStyleHints::startDragTime() const { - return themeableHint(QPlatformTheme::StartDragTime, QPlatformIntegration::StartDragTime).toInt(); + Q_D(const QStyleHints); + return d->m_startDragTime >= 0 ? + d->m_startDragTime : + themeableHint(QPlatformTheme::StartDragTime, QPlatformIntegration::StartDragTime).toInt(); } /*! @@ -143,12 +218,27 @@ int QStyleHints::startDragVelocity() const } /*! + Sets the \a keyboardInputInterval. + \internal + \sa keyboardInputInterval() + \since 5.3 +*/ +void QStyleHints::setKeyboardInputInterval(int keyboardInputInterval) +{ + Q_D(QStyleHints); + d->m_keyboardInputInterval = keyboardInputInterval; +} + +/*! Returns the time limit, in milliseconds, that distinguishes a key press from two consecutive key presses. */ int QStyleHints::keyboardInputInterval() const { - return themeableHint(QPlatformTheme::KeyboardInputInterval, QPlatformIntegration::KeyboardInputInterval).toInt(); + Q_D(const QStyleHints); + return d->m_keyboardInputInterval >= 0 ? + d->m_keyboardInputInterval : + themeableHint(QPlatformTheme::KeyboardInputInterval, QPlatformIntegration::KeyboardInputInterval).toInt(); } /*! @@ -161,6 +251,18 @@ int QStyleHints::keyboardAutoRepeatRate() const } /*! + Sets the \a cursorFlashTime. + \internal + \sa cursorFlashTime() + \since 5.3 +*/ +void QStyleHints::setCursorFlashTime(int cursorFlashTime) +{ + Q_D(QStyleHints); + d->m_cursorFlashTime = cursorFlashTime; +} + +/*! Returns the text cursor's flash (blink) time in milliseconds. The flash time is the time used to display, invert and restore the @@ -169,7 +271,10 @@ int QStyleHints::keyboardAutoRepeatRate() const */ int QStyleHints::cursorFlashTime() const { - return themeableHint(QPlatformTheme::CursorFlashTime, QPlatformIntegration::CursorFlashTime).toInt(); + Q_D(const QStyleHints); + return d->m_cursorFlashTime >= 0 ? + d->m_cursorFlashTime : + themeableHint(QPlatformTheme::CursorFlashTime, QPlatformIntegration::CursorFlashTime).toInt(); } /*! diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h index a0facd5f94..33fbe2965e 100644 --- a/src/gui/kernel/qstylehints.h +++ b/src/gui/kernel/qstylehints.h @@ -48,17 +48,25 @@ QT_BEGIN_NAMESPACE class QPlatformIntegration; +class QStyleHintsPrivate; class Q_GUI_EXPORT QStyleHints : public QObject { Q_OBJECT + Q_DECLARE_PRIVATE(QStyleHints) public: + void setMouseDoubleClickInterval(int mouseDoubleClickInterval); int mouseDoubleClickInterval() const; + int mousePressAndHoldInterval() const; + void setStartDragDistance(int startDragDistance); int startDragDistance() const; + void setStartDragTime(int startDragTime); int startDragTime() const; int startDragVelocity() const; + void setKeyboardInputInterval(int keyboardInputInterval); int keyboardInputInterval() const; int keyboardAutoRepeatRate() const; + void setCursorFlashTime(int cursorFlashTime); int cursorFlashTime() const; bool showIsFullScreen() const; int passwordMaskDelay() const; diff --git a/src/gui/kernel/qsurfaceformat.cpp b/src/gui/kernel/qsurfaceformat.cpp index fe5615d394..2b6cb2d949 100644 --- a/src/gui/kernel/qsurfaceformat.cpp +++ b/src/gui/kernel/qsurfaceformat.cpp @@ -72,6 +72,7 @@ public: , profile(QSurfaceFormat::NoProfile) , major(2) , minor(0) + , swapInterval(1) // default to vsync { } @@ -89,7 +90,8 @@ public: renderableType(other->renderableType), profile(other->profile), major(other->major), - minor(other->minor) + minor(other->minor), + swapInterval(other->swapInterval) { } @@ -107,6 +109,7 @@ public: QSurfaceFormat::OpenGLContextProfile profile; int major; int minor; + int swapInterval; }; /*! @@ -311,9 +314,15 @@ void QSurfaceFormat::setSamples(int numSamples) } /*! - Sets the format option to \a opt. + \obsolete + \overload - \sa testOption() + Use setOption(QSurfaceFormat::FormatOption, bool) or setOptions() instead. + + Sets the format options to the OR combination of \a opt and the + current format options. + + \sa options(), testOption() */ void QSurfaceFormat::setOption(QSurfaceFormat::FormatOptions opt) { @@ -325,7 +334,13 @@ void QSurfaceFormat::setOption(QSurfaceFormat::FormatOptions opt) } /*! - Returns \c true if format option \a opt is set; otherwise returns \c false. + \obsolete + \overload + + Use testOption(QSurfaceFormat::FormatOption) instead. + + Returns \c true if any of the options in \a opt is currently set + on this object; otherwise returns false. \sa setOption() */ @@ -335,6 +350,63 @@ bool QSurfaceFormat::testOption(QSurfaceFormat::FormatOptions opt) const } /*! + \since 5.3 + + Sets the format options to \a options. + + \sa options(), testOption() +*/ +void QSurfaceFormat::setOptions(QSurfaceFormat::FormatOptions options) +{ + if (int(d->opts) != int(options)) { + detach(); + d->opts = options; + } +} + +/*! + \since 5.3 + + Sets the format option \a option if \a on is true; otherwise, clears the option. + + \sa setOptions(), options(), testOption() +*/ +void QSurfaceFormat::setOption(QSurfaceFormat::FormatOption option, bool on) +{ + if (testOption(option) == on) + return; + detach(); + if (on) + d->opts |= option; + else + d->opts &= ~option; +} + +/*! + \since 5.3 + + Returns true if the format option \a option is set; otherwise returns false. + + \sa options(), testOption() +*/ +bool QSurfaceFormat::testOption(QSurfaceFormat::FormatOption option) const +{ + return d->opts & option; +} + +/*! + \since 5.3 + + Returns the currently set format options. + + \sa setOption(), setOptions(), testOption() +*/ +QSurfaceFormat::FormatOptions QSurfaceFormat::options() const +{ + return d->opts; +} + +/*! Set the minimum depth buffer size to \a size. \sa depthBufferSize() @@ -607,6 +679,46 @@ void QSurfaceFormat::setVersion(int major, int minor) } /*! + Sets the preferred swap interval. The swap interval specifies the + minimum number of video frames that are displayed before a buffer + swap occurs. This can be used to sync the GL drawing into a window + to the vertical refresh of the screen. + + Setting an \a interval value of 0 will turn the vertical refresh + syncing off, any value higher than 0 will turn the vertical + syncing on. Setting \a interval to a higher value, for example 10, + results in having 10 vertical retraces between every buffer swap. + + The default interval is 1. + + Changing the swap interval may not be supported by the underlying + platform. In this case, the request will be silently ignored. + + \since 5.3 + + \sa swapInterval() + */ +void QSurfaceFormat::setSwapInterval(int interval) +{ + if (d->swapInterval != interval) { + detach(); + d->swapInterval = interval; + } +} + +/*! + Returns the swap interval. + + \since 5.3 + + \sa setSwapInterval() +*/ +int QSurfaceFormat::swapInterval() const +{ + return d->swapInterval; +} + +/*! Returns \c true if all the options of the two QSurfaceFormat objects \a a and \a b are equal. @@ -625,7 +737,8 @@ bool operator==(const QSurfaceFormat& a, const QSurfaceFormat& b) && a.d->swapBehavior == b.d->swapBehavior && a.d->profile == b.d->profile && a.d->major == b.d->major - && a.d->minor == b.d->minor); + && a.d->minor == b.d->minor + && a.d->swapInterval == b.d->swapInterval); } /*! @@ -655,6 +768,7 @@ QDebug operator<<(QDebug dbg, const QSurfaceFormat &f) << ", stencilBufferSize " << d->stencilSize << ", samples " << d->numSamples << ", swapBehavior " << d->swapBehavior + << ", swapInterval " << d->swapInterval << ", profile " << d->profile << ')'; diff --git a/src/gui/kernel/qsurfaceformat.h b/src/gui/kernel/qsurfaceformat.h index 7c3c846df3..453beac5cd 100644 --- a/src/gui/kernel/qsurfaceformat.h +++ b/src/gui/kernel/qsurfaceformat.h @@ -127,8 +127,16 @@ public: bool stereo() const; void setStereo(bool enable); - void setOption(QSurfaceFormat::FormatOptions opt); - bool testOption(QSurfaceFormat::FormatOptions opt) const; + QT_DEPRECATED void setOption(QSurfaceFormat::FormatOptions opt); + QT_DEPRECATED bool testOption(QSurfaceFormat::FormatOptions opt) const; + + void setOptions(QSurfaceFormat::FormatOptions options); + void setOption(FormatOption option, bool on = true); + bool testOption(FormatOption option) const; + QSurfaceFormat::FormatOptions options() const; + + int swapInterval() const; + void setSwapInterval(int interval); private: QSurfaceFormatPrivate *d; diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 8ef275a27c..49ff8bcb0d 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -164,31 +164,35 @@ void QWindowSystemInterface::handleCloseEvent(QWindow *tlw, bool *accepted) \a w == 0 means that the event is in global coords only, \a local will be ignored in this case */ -void QWindowSystemInterface::handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +void QWindowSystemInterface::handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleMouseEvent(w, time, local, global, b, mods); + handleMouseEvent(w, time, local, global, b, mods, source); } -void QWindowSystemInterface::handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +void QWindowSystemInterface::handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { QWindowSystemInterfacePrivate::MouseEvent * e = - new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, local, global, b, mods); + new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, local, global, b, mods, source); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { const unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); - handleFrameStrutMouseEvent(w, time, local, global, b, mods); + handleFrameStrutMouseEvent(w, time, local, global, b, mods, source); } -void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods, Qt::MouseEventSource source) { QWindowSystemInterfacePrivate::MouseEvent * e = new QWindowSystemInterfacePrivate::MouseEvent(w, timestamp, QWindowSystemInterfacePrivate::FrameStrutMouse, - local, global, b, mods); + local, global, b, mods, source); QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index d1c3c8e249..71feb1bcb7 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -73,10 +73,18 @@ class QPlatformDropQtResponse; class Q_GUI_EXPORT QWindowSystemInterface { public: - static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier); - static void handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier); - static void handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier); - static void handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier); + static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods = Qt::NoModifier, + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + static void handleMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods = Qt::NoModifier, + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + static void handleFrameStrutMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods = Qt::NoModifier, + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); + static void handleFrameStrutMouseEvent(QWindow *w, ulong timestamp, const QPointF & local, const QPointF & global, Qt::MouseButtons b, + Qt::KeyboardModifiers mods = Qt::NoModifier, + Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); static bool tryHandleShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 42dbe7509e..8e503bbf3d 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -209,14 +209,17 @@ public: class MouseEvent : public InputEvent { public: MouseEvent(QWindow * w, ulong time, const QPointF & local, const QPointF & global, - Qt::MouseButtons b, Qt::KeyboardModifiers mods) - : InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b) { } + Qt::MouseButtons b, Qt::KeyboardModifiers mods, + Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) + : InputEvent(w, time, Mouse, mods), localPos(local), globalPos(global), buttons(b), source(src) { } MouseEvent(QWindow * w, ulong time, EventType t, const QPointF & local, const QPointF & global, - Qt::MouseButtons b, Qt::KeyboardModifiers mods) - : InputEvent(w, time, t, mods), localPos(local), globalPos(global), buttons(b) { } + Qt::MouseButtons b, Qt::KeyboardModifiers mods, + Qt::MouseEventSource src = Qt::MouseEventNotSynthesized) + : InputEvent(w, time, t, mods), localPos(local), globalPos(global), buttons(b), source(src) { } QPointF localPos; QPointF globalPos; Qt::MouseButtons buttons; + Qt::MouseEventSource source; }; class WheelEvent : public InputEvent { diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h index 55e606ec35..649d45d477 100644 --- a/src/gui/math3d/qvector2d.h +++ b/src/gui/math3d/qvector2d.h @@ -57,10 +57,10 @@ class QVariant; class Q_GUI_EXPORT QVector2D { public: - QVector2D(); - QVector2D(float xpos, float ypos); - explicit QVector2D(const QPoint& point); - explicit QVector2D(const QPointF& point); + Q_DECL_CONSTEXPR QVector2D(); + Q_DECL_CONSTEXPR QVector2D(float xpos, float ypos); + Q_DECL_CONSTEXPR explicit QVector2D(const QPoint& point); + Q_DECL_CONSTEXPR explicit QVector2D(const QPointF& point); #ifndef QT_NO_VECTOR3D explicit QVector2D(const QVector3D& vector); #endif @@ -70,8 +70,8 @@ public: bool isNull() const; - float x() const; - float y() const; + Q_DECL_CONSTEXPR float x() const; + Q_DECL_CONSTEXPR float y() const; void setX(float x); void setY(float y); @@ -80,7 +80,12 @@ public: float operator[](int i) const; float length() const; +#ifdef QT_BUILD_GUI_LIB float lengthSquared() const; +#else + Q_DECL_CONSTEXPR inline float lengthSquared() const + { return xp * xp + yp * yp; } +#endif QVector2D normalized() const; void normalize(); @@ -94,19 +99,24 @@ public: QVector2D &operator*=(const QVector2D &vector); QVector2D &operator/=(float divisor); +#ifdef QT_BUILD_GUI_LIB static float dotProduct(const QVector2D& v1, const QVector2D& v2); +#else + Q_DECL_CONSTEXPR inline static float dotProduct(const QVector2D& v1, const QVector2D& v2) + { return v1.xp * v2.xp + v1.yp * v2.yp; } +#endif - friend inline bool operator==(const QVector2D &v1, const QVector2D &v2); - friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2); - friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2); - friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2); - friend inline const QVector2D operator*(float factor, const QVector2D &vector); - friend inline const QVector2D operator*(const QVector2D &vector, float factor); - friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2); - friend inline const QVector2D operator-(const QVector2D &vector); - friend inline const QVector2D operator/(const QVector2D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline bool operator==(const QVector2D &v1, const QVector2D &v2); + Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2); + Q_DECL_CONSTEXPR friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2); + Q_DECL_CONSTEXPR friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2); + Q_DECL_CONSTEXPR friend inline const QVector2D operator*(float factor, const QVector2D &vector); + Q_DECL_CONSTEXPR friend inline const QVector2D operator*(const QVector2D &vector, float factor); + Q_DECL_CONSTEXPR friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2); + Q_DECL_CONSTEXPR friend inline const QVector2D operator-(const QVector2D &vector); + Q_DECL_CONSTEXPR friend inline const QVector2D operator/(const QVector2D &vector, float divisor); - friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2); + Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2); #ifndef QT_NO_VECTOR3D QVector3D toVector3D() const; @@ -115,8 +125,8 @@ public: QVector4D toVector4D() const; #endif - QPoint toPoint() const; - QPointF toPointF() const; + Q_DECL_CONSTEXPR QPoint toPoint() const; + Q_DECL_CONSTEXPR QPointF toPointF() const; operator QVariant() const; @@ -129,21 +139,21 @@ private: Q_DECLARE_TYPEINFO(QVector2D, Q_MOVABLE_TYPE); -inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {} -inline QVector2D::QVector2D(float xpos, float ypos) : xp(xpos), yp(ypos) {} +Q_DECL_CONSTEXPR inline QVector2D::QVector2D(float xpos, float ypos) : xp(xpos), yp(ypos) {} -inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {} +Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {} -inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {} +Q_DECL_CONSTEXPR inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {} inline bool QVector2D::isNull() const { return qIsNull(xp) && qIsNull(yp); } -inline float QVector2D::x() const { return xp; } -inline float QVector2D::y() const { return yp; } +Q_DECL_CONSTEXPR inline float QVector2D::x() const { return xp; } +Q_DECL_CONSTEXPR inline float QVector2D::y() const { return yp; } inline void QVector2D::setX(float aX) { xp = aX; } inline void QVector2D::setY(float aY) { yp = aY; } @@ -195,62 +205,62 @@ inline QVector2D &QVector2D::operator/=(float divisor) return *this; } -inline bool operator==(const QVector2D &v1, const QVector2D &v2) +Q_DECL_CONSTEXPR inline bool operator==(const QVector2D &v1, const QVector2D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp; } -inline bool operator!=(const QVector2D &v1, const QVector2D &v2) +Q_DECL_CONSTEXPR inline bool operator!=(const QVector2D &v1, const QVector2D &v2) { return v1.xp != v2.xp || v1.yp != v2.yp; } -inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) +Q_DECL_CONSTEXPR inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) { return QVector2D(v1.xp + v2.xp, v1.yp + v2.yp); } -inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) +Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) { return QVector2D(v1.xp - v2.xp, v1.yp - v2.yp); } -inline const QVector2D operator*(float factor, const QVector2D &vector) +Q_DECL_CONSTEXPR inline const QVector2D operator*(float factor, const QVector2D &vector) { return QVector2D(vector.xp * factor, vector.yp * factor); } -inline const QVector2D operator*(const QVector2D &vector, float factor) +Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &vector, float factor) { return QVector2D(vector.xp * factor, vector.yp * factor); } -inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) +Q_DECL_CONSTEXPR inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) { return QVector2D(v1.xp * v2.xp, v1.yp * v2.yp); } -inline const QVector2D operator-(const QVector2D &vector) +Q_DECL_CONSTEXPR inline const QVector2D operator-(const QVector2D &vector) { return QVector2D(-vector.xp, -vector.yp); } -inline const QVector2D operator/(const QVector2D &vector, float divisor) +Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, float divisor) { return QVector2D(vector.xp / divisor, vector.yp / divisor); } -inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) +Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp); } -inline QPoint QVector2D::toPoint() const +Q_DECL_CONSTEXPR inline QPoint QVector2D::toPoint() const { return QPoint(qRound(xp), qRound(yp)); } -inline QPointF QVector2D::toPointF() const +Q_DECL_CONSTEXPR inline QPointF QVector2D::toPointF() const { return QPointF(qreal(xp), qreal(yp)); } diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h index c880930935..c5506bf1ac 100644 --- a/src/gui/math3d/qvector3d.h +++ b/src/gui/math3d/qvector3d.h @@ -57,10 +57,11 @@ class QVector4D; class Q_GUI_EXPORT QVector3D { public: - QVector3D(); - QVector3D(float xpos, float ypos, float zpos); - explicit QVector3D(const QPoint& point); - explicit QVector3D(const QPointF& point); + Q_DECL_CONSTEXPR QVector3D(); + Q_DECL_CONSTEXPR QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {} + + Q_DECL_CONSTEXPR explicit QVector3D(const QPoint& point); + Q_DECL_CONSTEXPR explicit QVector3D(const QPointF& point); #ifndef QT_NO_VECTOR2D QVector3D(const QVector2D& vector); QVector3D(const QVector2D& vector, float zpos); @@ -71,9 +72,9 @@ public: bool isNull() const; - float x() const; - float y() const; - float z() const; + Q_DECL_CONSTEXPR float x() const; + Q_DECL_CONSTEXPR float y() const; + Q_DECL_CONSTEXPR float z() const; void setX(float x); void setY(float y); @@ -94,8 +95,17 @@ public: QVector3D &operator*=(const QVector3D& vector); QVector3D &operator/=(float divisor); +#ifdef QT_BUILD_GUI_LIB static float dotProduct(const QVector3D& v1, const QVector3D& v2); static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2); +#else + Q_DECL_CONSTEXPR inline static float dotProduct(const QVector3D& v1, const QVector3D& v2) + { return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp; } + Q_DECL_CONSTEXPR inline static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2) + { return QVector3D(v1.yp * v2.zp - v1.zp * v2.yp, + v1.zp * v2.xp - v1.xp * v2.zp, + v1.xp * v2.yp - v1.yp * v2.xp); } +#endif static QVector3D normal(const QVector3D& v1, const QVector3D& v2); static QVector3D normal (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3); @@ -105,17 +115,17 @@ public: float distanceToPlane(const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const; float distanceToLine(const QVector3D& point, const QVector3D& direction) const; - friend inline bool operator==(const QVector3D &v1, const QVector3D &v2); - friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2); - friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2); - friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2); - friend inline const QVector3D operator*(float factor, const QVector3D &vector); - friend inline const QVector3D operator*(const QVector3D &vector, float factor); - friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2); - friend inline const QVector3D operator-(const QVector3D &vector); - friend inline const QVector3D operator/(const QVector3D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline bool operator==(const QVector3D &v1, const QVector3D &v2); + Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2); + Q_DECL_CONSTEXPR friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2); + Q_DECL_CONSTEXPR friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2); + Q_DECL_CONSTEXPR friend inline const QVector3D operator*(float factor, const QVector3D &vector); + Q_DECL_CONSTEXPR friend inline const QVector3D operator*(const QVector3D &vector, float factor); + Q_DECL_CONSTEXPR friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2); + Q_DECL_CONSTEXPR friend inline const QVector3D operator-(const QVector3D &vector); + Q_DECL_CONSTEXPR friend inline const QVector3D operator/(const QVector3D &vector, float divisor); - friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2); + Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2); #ifndef QT_NO_VECTOR2D QVector2D toVector2D() const; @@ -124,8 +134,8 @@ public: QVector4D toVector4D() const; #endif - QPoint toPoint() const; - QPointF toPointF() const; + Q_DECL_CONSTEXPR QPoint toPoint() const; + Q_DECL_CONSTEXPR QPointF toPointF() const; operator QVariant() const; @@ -142,22 +152,20 @@ private: Q_DECLARE_TYPEINFO(QVector3D, Q_MOVABLE_TYPE); -inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {} - -inline QVector3D::QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {} +Q_DECL_CONSTEXPR inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {} -inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} -inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} inline bool QVector3D::isNull() const { return qIsNull(xp) && qIsNull(yp) && qIsNull(zp); } -inline float QVector3D::x() const { return xp; } -inline float QVector3D::y() const { return yp; } -inline float QVector3D::z() const { return zp; } +Q_DECL_CONSTEXPR inline float QVector3D::x() const { return xp; } +Q_DECL_CONSTEXPR inline float QVector3D::y() const { return yp; } +Q_DECL_CONSTEXPR inline float QVector3D::z() const { return zp; } inline void QVector3D::setX(float aX) { xp = aX; } inline void QVector3D::setY(float aY) { yp = aY; } @@ -215,64 +223,64 @@ inline QVector3D &QVector3D::operator/=(float divisor) return *this; } -inline bool operator==(const QVector3D &v1, const QVector3D &v2) +Q_DECL_CONSTEXPR inline bool operator==(const QVector3D &v1, const QVector3D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp; } -inline bool operator!=(const QVector3D &v1, const QVector3D &v2) +Q_DECL_CONSTEXPR inline bool operator!=(const QVector3D &v1, const QVector3D &v2) { return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp; } -inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) +Q_DECL_CONSTEXPR inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) { return QVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp); } -inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) +Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) { return QVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp); } -inline const QVector3D operator*(float factor, const QVector3D &vector) +Q_DECL_CONSTEXPR inline const QVector3D operator*(float factor, const QVector3D &vector) { return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor); } -inline const QVector3D operator*(const QVector3D &vector, float factor) +Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &vector, float factor) { return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor); } -inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) +Q_DECL_CONSTEXPR inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) { return QVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp); } -inline const QVector3D operator-(const QVector3D &vector) +Q_DECL_CONSTEXPR inline const QVector3D operator-(const QVector3D &vector) { return QVector3D(-vector.xp, -vector.yp, -vector.zp); } -inline const QVector3D operator/(const QVector3D &vector, float divisor) +Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, float divisor) { return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor); } -inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) +Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp) && qFuzzyCompare(v1.zp, v2.zp); } -inline QPoint QVector3D::toPoint() const +Q_DECL_CONSTEXPR inline QPoint QVector3D::toPoint() const { return QPoint(qRound(xp), qRound(yp)); } -inline QPointF QVector3D::toPointF() const +Q_DECL_CONSTEXPR inline QPointF QVector3D::toPointF() const { return QPointF(qreal(xp), qreal(yp)); } diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h index 810380b805..1256f384a0 100644 --- a/src/gui/math3d/qvector4d.h +++ b/src/gui/math3d/qvector4d.h @@ -57,10 +57,10 @@ class QVector3D; class Q_GUI_EXPORT QVector4D { public: - QVector4D(); - QVector4D(float xpos, float ypos, float zpos, float wpos); - explicit QVector4D(const QPoint& point); - explicit QVector4D(const QPointF& point); + Q_DECL_CONSTEXPR QVector4D(); + Q_DECL_CONSTEXPR QVector4D(float xpos, float ypos, float zpos, float wpos); + Q_DECL_CONSTEXPR explicit QVector4D(const QPoint& point); + Q_DECL_CONSTEXPR explicit QVector4D(const QPointF& point); #ifndef QT_NO_VECTOR2D QVector4D(const QVector2D& vector); QVector4D(const QVector2D& vector, float zpos, float wpos); @@ -72,10 +72,10 @@ public: bool isNull() const; - float x() const; - float y() const; - float z() const; - float w() const; + Q_DECL_CONSTEXPR float x() const; + Q_DECL_CONSTEXPR float y() const; + Q_DECL_CONSTEXPR float z() const; + Q_DECL_CONSTEXPR float w() const; void setX(float x); void setY(float y); @@ -86,7 +86,12 @@ public: float operator[](int i) const; float length() const; +#ifdef QT_BUILD_GUI_LIB float lengthSquared() const; +#else + Q_DECL_CONSTEXPR inline float lengthSquared() const + { return xp * xp + yp * yp + zp * zp + wp * wp; } +#endif QVector4D normalized() const; void normalize(); @@ -97,19 +102,24 @@ public: QVector4D &operator*=(const QVector4D &vector); QVector4D &operator/=(float divisor); +#ifdef QT_BUILD_GUI_LIB static float dotProduct(const QVector4D& v1, const QVector4D& v2); +#else + static float dotProduct(const QVector4D& v1, const QVector4D& v2) + { return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp + v1.wp * v2.wp; } +#endif - friend inline bool operator==(const QVector4D &v1, const QVector4D &v2); - friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2); - friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2); - friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2); - friend inline const QVector4D operator*(float factor, const QVector4D &vector); - friend inline const QVector4D operator*(const QVector4D &vector, float factor); - friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2); - friend inline const QVector4D operator-(const QVector4D &vector); - friend inline const QVector4D operator/(const QVector4D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline bool operator==(const QVector4D &v1, const QVector4D &v2); + Q_DECL_CONSTEXPR friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2); + Q_DECL_CONSTEXPR friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2); + Q_DECL_CONSTEXPR friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2); + Q_DECL_CONSTEXPR friend inline const QVector4D operator*(float factor, const QVector4D &vector); + Q_DECL_CONSTEXPR friend inline const QVector4D operator*(const QVector4D &vector, float factor); + Q_DECL_CONSTEXPR friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2); + Q_DECL_CONSTEXPR friend inline const QVector4D operator-(const QVector4D &vector); + Q_DECL_CONSTEXPR friend inline const QVector4D operator/(const QVector4D &vector, float divisor); - friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2); + Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2); #ifndef QT_NO_VECTOR2D QVector2D toVector2D() const; @@ -120,8 +130,8 @@ public: QVector3D toVector3DAffine() const; #endif - QPoint toPoint() const; - QPointF toPointF() const; + Q_DECL_CONSTEXPR QPoint toPoint() const; + Q_DECL_CONSTEXPR QPointF toPointF() const; operator QVariant() const; @@ -138,23 +148,23 @@ private: Q_DECLARE_TYPEINFO(QVector4D, Q_MOVABLE_TYPE); -inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {} -inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} +Q_DECL_CONSTEXPR inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} -inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} -inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} +Q_DECL_CONSTEXPR inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} inline bool QVector4D::isNull() const { return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp); } -inline float QVector4D::x() const { return xp; } -inline float QVector4D::y() const { return yp; } -inline float QVector4D::z() const { return zp; } -inline float QVector4D::w() const { return wp; } +Q_DECL_CONSTEXPR inline float QVector4D::x() const { return xp; } +Q_DECL_CONSTEXPR inline float QVector4D::y() const { return yp; } +Q_DECL_CONSTEXPR inline float QVector4D::z() const { return zp; } +Q_DECL_CONSTEXPR inline float QVector4D::w() const { return wp; } inline void QVector4D::setX(float aX) { xp = aX; } inline void QVector4D::setY(float aY) { yp = aY; } @@ -218,52 +228,52 @@ inline QVector4D &QVector4D::operator/=(float divisor) return *this; } -inline bool operator==(const QVector4D &v1, const QVector4D &v2) +Q_DECL_CONSTEXPR inline bool operator==(const QVector4D &v1, const QVector4D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp && v1.wp == v2.wp; } -inline bool operator!=(const QVector4D &v1, const QVector4D &v2) +Q_DECL_CONSTEXPR inline bool operator!=(const QVector4D &v1, const QVector4D &v2) { return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp || v1.wp != v2.wp; } -inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) +Q_DECL_CONSTEXPR inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) { return QVector4D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, v1.wp + v2.wp); } -inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) +Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) { return QVector4D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, v1.wp - v2.wp); } -inline const QVector4D operator*(float factor, const QVector4D &vector) +Q_DECL_CONSTEXPR inline const QVector4D operator*(float factor, const QVector4D &vector) { return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor); } -inline const QVector4D operator*(const QVector4D &vector, float factor) +Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &vector, float factor) { return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor); } -inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) +Q_DECL_CONSTEXPR inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) { return QVector4D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, v1.wp * v2.wp); } -inline const QVector4D operator-(const QVector4D &vector) +Q_DECL_CONSTEXPR inline const QVector4D operator-(const QVector4D &vector) { return QVector4D(-vector.xp, -vector.yp, -vector.zp, -vector.wp); } -inline const QVector4D operator/(const QVector4D &vector, float divisor) +Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, float divisor) { return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor); } -inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) +Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp) && @@ -271,12 +281,12 @@ inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) qFuzzyCompare(v1.wp, v2.wp); } -inline QPoint QVector4D::toPoint() const +Q_DECL_CONSTEXPR inline QPoint QVector4D::toPoint() const { return QPoint(qRound(xp), qRound(yp)); } -inline QPointF QVector4D::toPointF() const +Q_DECL_CONSTEXPR inline QPointF QVector4D::toPointF() const { return QPointF(qreal(xp), qreal(yp)); } diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index c06ba40b47..a1c4cf2a29 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -446,42 +446,12 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi funcs.glGenFramebuffers(1, &fbo); funcs.glBindFramebuffer(GL_FRAMEBUFFER, fbo); - GLuint texture = 0; GLuint color_buffer = 0; QT_CHECK_GLERROR(); // init texture if (samples == 0) { - glGenTextures(1, &texture); - glBindTexture(target, texture); - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (mipmap) { - int width = size.width(); - int height = size.height(); - int level = 0; - while (width > 1 || height > 1) { - width = qMax(1, width >> 1); - height = qMax(1, height >> 1); - ++level; - glTexImage2D(target, level, internal_format, width, height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); - } - } - funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - target, texture, 0); - - QT_CHECK_GLERROR(); - valid = checkFramebufferStatus(ctx); - glBindTexture(target, 0); - - color_buffer = 0; + initTexture(texture_target, internal_format, size, mipmap); } else { mipmap = false; funcs.glGenRenderbuffers(1, &color_buffer); @@ -492,8 +462,10 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi QT_CHECK_GLERROR(); valid = checkFramebufferStatus(ctx); - if (valid) + if (valid) { funcs.glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); + color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); + } } format.setTextureTarget(target); @@ -506,20 +478,59 @@ void QOpenGLFramebufferObjectPrivate::init(QOpenGLFramebufferObject *, const QSi funcs.glBindFramebuffer(GL_FRAMEBUFFER, ctx->d_func()->current_fbo); if (valid) { fbo_guard = new QOpenGLSharedResourceGuard(ctx, fbo, freeFramebufferFunc); - if (color_buffer) - color_buffer_guard = new QOpenGLSharedResourceGuard(ctx, color_buffer, freeRenderbufferFunc); - else - texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc); } else { - if (color_buffer) - funcs.glDeleteRenderbuffers(1, &color_buffer); - else - glDeleteTextures(1, &texture); + if (color_buffer_guard) { + color_buffer_guard->free(); + color_buffer_guard = 0; + } else if (texture_guard) { + texture_guard->free(); + texture_guard = 0; + } funcs.glDeleteFramebuffers(1, &fbo); } QT_CHECK_GLERROR(); } +void QOpenGLFramebufferObjectPrivate::initTexture(GLenum target, GLenum internal_format, + const QSize &size, bool mipmap) +{ + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + GLuint texture = 0; + + glGenTextures(1, &texture); + glBindTexture(target, texture); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(target, 0, internal_format, size.width(), size.height(), 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (mipmap) { + int width = size.width(); + int height = size.height(); + int level = 0; + while (width > 1 || height > 1) { + width = qMax(1, width >> 1); + height = qMax(1, height >> 1); + ++level; + glTexImage2D(target, level, internal_format, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + } + funcs.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + target, texture, 0); + + QT_CHECK_GLERROR(); + glBindTexture(target, 0); + valid = checkFramebufferStatus(ctx); + if (valid) + texture_guard = new QOpenGLSharedResourceGuard(ctx, texture, freeTextureFunc); + else + glDeleteTextures(1, &texture); +} + void QOpenGLFramebufferObjectPrivate::initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment) { int samples = format.samples(); @@ -905,12 +916,16 @@ bool QOpenGLFramebufferObject::isValid() const framebuffer to this framebuffer object. Returns \c true upon success, false otherwise. + \note If takeTexture() was called, a new texture is created and associated + with the framebuffer object. This is potentially expensive and changes the + context state (the currently bound texture). + \sa release() */ bool QOpenGLFramebufferObject::bind() { if (!isValid()) - return false; + return false; Q_D(QOpenGLFramebufferObject); QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) @@ -920,7 +935,10 @@ bool QOpenGLFramebufferObject::bind() qWarning("QOpenGLFramebufferObject::bind() called from incompatible context"); #endif d->funcs.glBindFramebuffer(GL_FRAMEBUFFER, d->fbo()); - d->valid = d->checkFramebufferStatus(current); + if (d->texture_guard || d->format.samples() != 0) + d->valid = d->checkFramebufferStatus(current); + else + d->initTexture(d->format.textureTarget(), d->format.internalTextureFormat(), d->size, d->format.mipmap()); if (d->valid && current) current->d_func()->current_fbo = d->fbo(); return d->valid; @@ -938,7 +956,7 @@ bool QOpenGLFramebufferObject::bind() bool QOpenGLFramebufferObject::release() { if (!isValid()) - return false; + return false; QOpenGLContext *current = QOpenGLContext::currentContext(); if (!current) @@ -967,6 +985,8 @@ bool QOpenGLFramebufferObject::release() If a multisample framebuffer object is used then the value returned from this function will be invalid. + + \sa takeTexture() */ GLuint QOpenGLFramebufferObject::texture() const { @@ -975,6 +995,40 @@ GLuint QOpenGLFramebufferObject::texture() const } /*! + \fn GLuint QOpenGLFramebufferObject::takeTexture() + + Returns the texture id for the texture attached to this framebuffer + object. The ownership of the texture is transferred to the caller. + + If the framebuffer object is currently bound, an implicit release() + will be done. During the next call to bind() a new texture will be + created. + + If a multisample framebuffer object is used, then there is no + texture and the return value from this function will be invalid. + Similarly, incomplete framebuffer objects will also return 0. + + \since 5.3 + + \sa texture(), bind(), release() + */ +GLuint QOpenGLFramebufferObject::takeTexture() +{ + Q_D(QOpenGLFramebufferObject); + GLuint id = 0; + if (isValid() && d->texture_guard) { + QOpenGLContext *current = QOpenGLContext::currentContext(); + if (current && current->shareGroup() == d->fbo_guard->group() && current->d_func()->current_fbo == d->fbo()) + release(); + id = d->texture_guard->id(); + // Do not call free() on texture_guard, just null it out. + // This way the texture will not be deleted when the guard is destroyed. + d->texture_guard = 0; + } + return id; +} + +/*! \fn QSize QOpenGLFramebufferObject::size() const Returns the size of the texture attached to this framebuffer diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h index 215d3701ca..a431618f6d 100644 --- a/src/gui/opengl/qopenglframebufferobject.h +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -97,6 +97,7 @@ public: int height() const { return size().height(); } GLuint texture() const; + GLuint takeTexture(); QSize size() const; QImage toImage() const; Attachment attachment() const; diff --git a/src/gui/opengl/qopenglframebufferobject_p.h b/src/gui/opengl/qopenglframebufferobject_p.h index c8c69c4b3e..1c7d2e1e5d 100644 --- a/src/gui/opengl/qopenglframebufferobject_p.h +++ b/src/gui/opengl/qopenglframebufferobject_p.h @@ -116,6 +116,7 @@ public: QOpenGLFramebufferObject::Attachment attachment, GLenum internal_format, GLenum texture_target, GLint samples = 0, bool mipmap = false); + void initTexture(GLenum target, GLenum internal_format, const QSize &size, bool mipmap); void initAttachments(QOpenGLContext *ctx, QOpenGLFramebufferObject::Attachment attachment); bool checkFramebufferStatus(QOpenGLContext *ctx) const; diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index aadcc0f686..6bf80eddbd 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -87,14 +87,15 @@ SOURCES += \ painting/qpaintbuffer.cpp \ painting/qpathsimplifier.cpp -SSE2_SOURCES += painting/qdrawhelper_sse2.cpp -SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp +contains(QT_CPU_FEATURES.$$QT_ARCH, sse2) { + SOURCES += painting/qdrawhelper_sse2.cpp + SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp +} IWMMXT_SOURCES += painting/qdrawhelper_iwmmxt.cpp -AVX_SOURCES += painting/qdrawhelper_avx.cpp -!ios { - NEON_SOURCES += painting/qdrawhelper_neon.cpp - NEON_HEADERS += painting/qdrawhelper_neon_p.h +!ios:contains(QT_CPU_FEATURES.$$QT_ARCH, neon) { + SOURCES += painting/qdrawhelper_neon.cpp + HEADERS += painting/qdrawhelper_neon_p.h NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S } diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 8bbe6b6f42..b35fa38ce0 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -56,44 +56,49 @@ QT_BEGIN_NAMESPACE const uchar *qt_patternForBrush(int brushStyle, bool invert) { Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern); - if(invert) { - static const uchar dense1_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff }; - static const uchar dense2_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff }; - static const uchar dense3_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }; - static const uchar dense4_pat[] = { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }; - static const uchar dense5_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 }; - static const uchar dense6_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }; - static const uchar dense7_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 }; - static const uchar hor_pat[] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }; - static const uchar ver_pat[] = { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }; - static const uchar cross_pat[] = { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 }; - static const uchar bdiag_pat[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; - static const uchar fdiag_pat[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; - static const uchar dcross_pat[] = { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }; - static const uchar *const pat_tbl[] = { - dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, - dense6_pat, dense7_pat, - hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; - return pat_tbl[brushStyle - Qt::Dense1Pattern]; - } - static const uchar dense1_pat[] = { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 }; - static const uchar dense2_pat[] = { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }; - static const uchar dense3_pat[] = { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 }; - static const uchar dense4_pat[] = { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }; - static const uchar dense5_pat[] = { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }; - static const uchar dense6_pat[] = { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff }; - static const uchar dense7_pat[] = { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff }; - static const uchar hor_pat[] = { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff }; - static const uchar ver_pat[] = { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }; - static const uchar cross_pat[] = { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef }; - static const uchar bdiag_pat[] = { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe }; - static const uchar fdiag_pat[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }; - static const uchar dcross_pat[] = { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e }; - static const uchar *const pat_tbl[] = { - dense1_pat, dense2_pat, dense3_pat, dense4_pat, dense5_pat, - dense6_pat, dense7_pat, - hor_pat, ver_pat, cross_pat, bdiag_pat, fdiag_pat, dcross_pat }; - return pat_tbl[brushStyle - Qt::Dense1Pattern]; + static const uchar pat_tbl[][2][8] = { + { + /* dense1 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 }, + /*~dense1 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff }, + }, { + /* dense2 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }, + /*~dense2 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff }, + }, { + /* dense3 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 }, + /*~dense3 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }, + }, { + /* dense4 */ { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa }, + /*~dense4 */ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, + }, { + /* dense5 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee }, + /*~dense5 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 }, + }, { + /* dense6 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff }, + /*~dense6 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }, + }, { + /* dense7 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff }, + /*~dense7 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 }, + }, { + /* hor */ { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff }, + /*~hor */ { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, + }, { + /* ver */ { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef }, + /*~ver */ { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }, + }, { + /* cross */ { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef }, + /*~cross */ { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 }, + }, { + /* bdiag */ { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe }, + /*~bdiag */ { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, + }, { + /* fdiag */ { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f }, + /*~fdiag */ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, + }, { + /* dcross */ { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e }, + /*~dcross */ { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, + }, + }; + return pat_tbl[brushStyle - Qt::Dense1Pattern][invert]; } QPixmap qt_pixmapForBrush(int brushStyle, bool invert) diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index d3e5b645c4..39193dd093 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -53,6 +53,13 @@ # endif #endif +#include <qglobal.h> +#ifdef Q_OS_IOS +// We don't build the NEON drawhelpers as they are implemented partly +// in GAS syntax assembly, which is not supported by the iOS toolchain. +#undef __ARM_NEON__ +#endif + #include <qstylehints.h> #include <qguiapplication.h> #include <qatomic.h> @@ -102,7 +109,7 @@ static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int, return src; } -static const uint *QT_FASTCALL convertRGB16ToARGB32PM(uint *buffer, const uint *src, int count, +static const uint *QT_FASTCALL convertRGB16ToRGB32(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *) { for (int i = 0; i < count; ++i) @@ -118,6 +125,22 @@ static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint return buffer; } +static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = RGBA2ARGB(src[i]); + return buffer; +} + +static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = PREMUL(RGBA2ARGB(src[i])); + return buffer; +} + static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count, const QPixelLayout *layout, const QRgb *) { @@ -206,6 +229,14 @@ static const uint *QT_FASTCALL convertToARGB32PM(uint *buffer, const uint *src, return buffer; } +static const uint *QT_FASTCALL convertRGB16FromRGB32(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = qConvertRgb32To16(src[i]); + return buffer; +} + static const uint *QT_FASTCALL convertRGB16FromARGB32PM(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *) { @@ -222,6 +253,38 @@ static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uin return buffer; } +static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = ARGB2RGBA(src[i]); + return buffer; +} + +static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = ARGB2RGBA(INV_PREMUL(src[i])); + return buffer; +} + +static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = ARGB2RGBA(0xff000000 | src[i]); + return buffer; +} + +static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = ARGB2RGBA(0xff000000 | INV_PREMUL(src[i])); + return buffer; +} + static const uint *QT_FASTCALL convertFromARGB32PM(uint *buffer, const uint *src, int count, const QPixelLayout *layout, const QRgb *) { @@ -242,7 +305,7 @@ static const uint *QT_FASTCALL convertFromARGB32PM(uint *buffer, const uint *src if (!layout->premultiplied) { for (int i = 0; i < count; ++i) - buffer[i] = qAlpha(src[i]) == 255 ? src[i] : INV_PREMUL(src[i]); + buffer[i] = INV_PREMUL(src[i]); src = buffer; } for (int i = 0; i < count; ++i) { @@ -255,13 +318,14 @@ static const uint *QT_FASTCALL convertFromARGB32PM(uint *buffer, const uint *src return buffer; } -static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *src, int count, - const QPixelLayout *layout, const QRgb *) +static const uint *QT_FASTCALL convertFromRGB32(uint *buffer, const uint *src, int count, + const QPixelLayout *layout, const QRgb *) { Q_ASSERT(layout->redWidth <= 8); Q_ASSERT(layout->greenWidth <= 8); Q_ASSERT(layout->blueWidth <= 8); Q_ASSERT(layout->alphaWidth == 0); + Q_ASSERT(!layout->premultiplied); const uint redMask = (1 << layout->redWidth) - 1; const uint greenMask = (1 << layout->greenWidth) - 1; @@ -272,12 +336,10 @@ static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint * const uchar blueRightShift = 8 - layout->blueWidth; for (int i = 0; i < count; ++i) { - uint color = INV_PREMUL(src[i]); - uint red = ((color >> redRightShift) & redMask) << layout->redShift; - uint green = ((color >> greenRightShift) & greenMask) << layout->greenShift; - uint blue = ((color >> blueRightShift) & blueMask) << layout->blueShift; - uint alpha = 0xff << layout->alphaShift; - buffer[i] = red | green | blue | alpha; + uint red = ((src[i] >> redRightShift) & redMask) << layout->redShift; + uint green = ((src[i] >> greenRightShift) & greenMask) << layout->greenShift; + uint blue = ((src[i] >> blueRightShift) & blueMask) << layout->blueShift; + buffer[i] = red | green | blue; } return buffer; } @@ -392,30 +454,32 @@ inline void QT_FASTCALL storePixels<QPixelLayout::BPP32>(uchar *dest, const uint // convertFromArgb32() assumes that no color channel is more than 8 bits. // QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits. QPixelLayout qPixelLayouts[QImage::NImageFormats] = { - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPPNone, 0, 0 }, // Format_Invalid - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1MSB, convertIndexedToARGB32PM, 0 }, // Format_Mono - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1LSB, convertIndexedToARGB32PM, 0 }, // Format_MonoLSB - { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertIndexedToARGB32PM, 0 }, // Format_Indexed8 - { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP32, convertPassThrough, convertPassThrough }, // Format_RGB32 - { 8, 16, 8, 8, 8, 0, 8, 24, false, QPixelLayout::BPP32, convertARGB32ToARGB32PM, convertARGB32FromARGB32PM }, // Format_ARGB32 - { 8, 16, 8, 8, 8, 0, 8, 24, true, QPixelLayout::BPP32, convertPassThrough, convertPassThrough }, // Format_ARGB32_Premultiplied - { 5, 11, 6, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, convertRGB16ToARGB32PM, convertRGB16FromARGB32PM }, // Format_RGB16 - { 5, 19, 6, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM }, // Format_ARGB8565_Premultiplied - { 6, 12, 6, 6, 6, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM }, // Format_RGB666 - { 6, 12, 6, 6, 6, 0, 6, 18, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM }, // Format_ARGB6666_Premultiplied - { 5, 10, 5, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM }, // Format_RGB555 - { 5, 18, 5, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM }, // Format_ARGB8555_Premultiplied - { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM }, // Format_RGB888 - { 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM }, // Format_RGB444 - { 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16, convertToARGB32PM, convertFromARGB32PM }, // Format_ARGB4444_Premultiplied + { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPPNone, 0, 0, 0 }, // Format_Invalid + { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1MSB, convertIndexedToARGB32PM, 0, 0 }, // Format_Mono + { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP1LSB, convertIndexedToARGB32PM, 0, 0 }, // Format_MonoLSB + { 0, 0, 0, 0, 0, 0, 0, 0, false, QPixelLayout::BPP8, convertIndexedToARGB32PM, 0, 0 }, // Format_Indexed8 + // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong, + // but everywhere this generic conversion would be wrong is currently overloaed. + { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough }, // Format_RGB32 + { 8, 16, 8, 8, 8, 0, 8, 24, false, QPixelLayout::BPP32, convertARGB32ToARGB32PM, convertARGB32FromARGB32PM, 0 }, // Format_ARGB32 + { 8, 16, 8, 8, 8, 0, 8, 24, true, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, 0 }, // Format_ARGB32_Premultiplied + { 5, 11, 6, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, convertRGB16ToRGB32, convertRGB16FromARGB32PM, convertRGB16FromRGB32 }, // Format_RGB16 + { 5, 19, 6, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB8565_Premultiplied + { 6, 12, 6, 6, 6, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB666 + { 6, 12, 6, 6, 6, 0, 6, 18, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB6666_Premultiplied + { 5, 10, 5, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB555 + { 5, 18, 5, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB8555_Premultiplied + { 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB888 + { 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB444 + { 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB4444_Premultiplied #if Q_BYTE_ORDER == Q_BIG_ENDIAN - { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertToRGB32, convertRGBFromARGB32PM }, // Format_RGBX8888 - { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888 - { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888_Premultiplied + { 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32 }, // Format_RGBX8888 + { 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, 0 }, // Format_RGBA8888 + { 8, 24, 8, 16, 8, 8, 8, 0, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, 0 }, // Format_RGBA8888_Premultiplied #else - { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertToRGB32, convertRGBFromARGB32PM }, // Format_RGBX8888 - { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM }, // Format_RGBA8888 (ABGR32) - { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertToARGB32PM, convertFromARGB32PM } // Format_RGBA8888_Premultiplied + { 8, 0, 8, 8, 8, 16, 0, 24, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32 }, // Format_RGBX8888 + { 8, 0, 8, 8, 8, 16, 8, 24, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, 0 }, // Format_RGBA8888 (ABGR32) + { 8, 0, 8, 8, 8, 16, 8, 24, true, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBA8888PMFromARGB32PM, 0 } // Format_RGBA8888_Premultiplied #endif }; @@ -630,7 +694,12 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con uchar *dest = rasterBuffer->scanLine(y); while (length) { int l = qMin(length, buffer_size); - const uint *ptr = layout->convertFromARGB32PM(buf, buffer, l, layout, 0); + const uint *ptr = 0; + if (layout->convertFromRGB32) { + Q_ASSERT(!layout->premultiplied && !layout->alphaWidth); + ptr = layout->convertFromRGB32(buf, buffer, l, layout, 0); + } else + ptr = layout->convertFromARGB32PM(buf, buffer, l, layout, 0); store(dest, ptr, x, l); length -= l; buffer += l; @@ -5071,13 +5140,13 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us int l = qMin(length, buffer_size); const uint *end = buffer + l; uint *b = buffer; + int px16 = x % (image_width << 16); + int py16 = y % (image_height << 16); + int px_delta = fdx % (image_width << 16); + int py_delta = fdy % (image_height << 16); while (b < end) { - int px = x >> 16; - int py = y >> 16; - px %= image_width; - py %= image_height; - if (px < 0) px += image_width; - if (py < 0) py += image_height; + int px = px16 >> 16; + int py = py16 >> 16; int y_offset = py * scanline_offset; Q_ASSERT(px >= 0 && px < image_width); @@ -5086,6 +5155,14 @@ static void blend_transformed_tiled_argb(int count, const QSpan *spans, void *us *b = image_bits[y_offset + px]; x += fdx; y += fdy; + px16 += px_delta; + if (px16 >= image_width << 16) + px16 -= image_width << 16; + py16 += py_delta; + if (py16 >= image_height << 16) + py16 -= image_height << 16; + if (px16 < 0) px16 += image_width << 16; + if (py16 < 0) py16 += image_height << 16; ++b; } func(target, buffer, l, coverage); @@ -6140,110 +6217,73 @@ inline void qt_memfill_template(quint16 *dest, quint16 value, int count) } #endif -static void qt_memfill_quint16(quint16 *dest, quint16 color, int count) +#if !defined(__SSE2__) +void qt_memfill16(quint16 *dest, quint16 color, int count) { qt_memfill_template<quint16>(dest, color, count); } - -typedef void (*qt_memfill32_func)(quint32 *dest, quint32 value, int count); -typedef void (*qt_memfill16_func)(quint16 *dest, quint16 value, int count); -static void qt_memfill32_setup(quint32 *dest, quint32 value, int count); -static void qt_memfill16_setup(quint16 *dest, quint16 value, int count); - -qt_memfill32_func qt_memfill32 = qt_memfill32_setup; -qt_memfill16_func qt_memfill16 = qt_memfill16_setup; +#endif +#if !defined(__SSE2__) && !defined(__ARM_NEON__) +void qt_memfill32(quint32 *dest, quint32 color, int count) +{ +# ifdef QT_COMPILER_SUPPORTS_MIPS_DSP + extern "C" qt_memfill32_asm_mips_dsp(quint32 *, quint32, int); + qt_memfill32_asm_mips_dsp(dest, color, count); +# else + qt_memfill_template<quint32>(dest, color, count); +# endif +} +#endif void qInitDrawhelperAsm() { - - qt_memfill32 = qt_memfill_template<quint32>; - qt_memfill16 = qt_memfill_quint16; //qt_memfill_template<quint16>; - CompositionFunction *functionForModeAsm = 0; CompositionFunctionSolid *functionForModeSolidAsm = 0; const uint features = qCpuFeatures(); - if (false) { - Q_UNUSED(features); -#ifdef QT_COMPILER_SUPPORTS_AVX - } else if (features & AVX) { - qt_memfill32 = qt_memfill32_avx; - qt_memfill16 = qt_memfill16_avx; - qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_avx; - qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_avx; - qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_avx; - qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_avx; - qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit32_avx; - qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit32_avx; - qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit32_avx; - - extern void qt_scale_image_argb32_on_argb32_avx(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - const QRectF &targetRect, - const QRectF &sourceRect, - const QRect &clip, - int const_alpha); - qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_avx; - qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_avx; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_avx; - qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_avx; -#endif -#endif -#ifdef QT_COMPILER_SUPPORTS_SSE2 - } else if (features & SSE2) { - qt_memfill32 = qt_memfill32_sse2; - qt_memfill16 = qt_memfill16_sse2; - qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2; - qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2; - qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; - qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2; - qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit32_sse2; - qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit32_sse2; - qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; - - extern void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - const QRectF &targetRect, - const QRectF &sourceRect, - const QRect &clip, - int const_alpha); - qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; - qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; - qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; -#endif -#endif - } - -#ifdef QT_COMPILER_SUPPORTS_SSE2 - if (features & SSE2) { - extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2; - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2; -#endif - - extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length); - - qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2; - } + Q_UNUSED(features); +#ifdef __SSE2__ + qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2; + qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; + + extern void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + const QRectF &targetRect, + const QRectF &sourceRect, + const QRect &clip, + int const_alpha); + qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; + qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; + qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; + qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2; + + extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + + extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length); + + qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2; #ifdef QT_COMPILER_SUPPORTS_SSSE3 if (features & SSSE3) { @@ -6254,65 +6294,13 @@ void qInitDrawhelperAsm() qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; -#endif } #endif // SSSE3 -#ifdef QT_COMPILER_SUPPORTS_AVX - if (features & AVX) { - extern void qt_blend_rgb32_on_rgb32_avx(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - extern void qt_blend_argb32_on_argb32_avx(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); - - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx; - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx; -#endif - - extern const uint * QT_FASTCALL qt_fetch_radial_gradient_avx(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length); - - qt_fetch_radial_gradient = qt_fetch_radial_gradient_avx; - } -#endif // AVX - -#endif // SSE2 - -#ifdef QT_COMPILER_SUPPORTS_SSE2 - if (features & SSE2) { - functionForModeAsm = qt_functionForMode_SSE2; - functionForModeSolidAsm = qt_functionForModeSolid_SSE2; - } -#endif -#ifdef QT_COMPILER_SUPPORTS_AVX - if (features & AVX) { - extern void QT_FASTCALL comp_func_SourceOver_avx(uint *destPixels, - const uint *srcPixels, - int length, - uint const_alpha); - extern void QT_FASTCALL comp_func_solid_SourceOver_avx(uint *destPixels, int length, uint color, uint const_alpha); - extern void QT_FASTCALL comp_func_Plus_avx(uint *dst, const uint *src, int length, uint const_alpha); - extern void QT_FASTCALL comp_func_Source_avx(uint *dst, const uint *src, int length, uint const_alpha); - - functionForModeAsm[0] = comp_func_SourceOver_avx; - functionForModeAsm[QPainter::CompositionMode_Source] = comp_func_Source_avx; - functionForModeAsm[QPainter::CompositionMode_Plus] = comp_func_Plus_avx; - functionForModeSolidAsm[0] = comp_func_solid_SourceOver_avx; - } + functionForModeAsm = qt_functionForMode_SSE2; + functionForModeSolidAsm = qt_functionForModeSolid_SSE2; #endif // SSE2 #ifdef QT_COMPILER_SUPPORTS_IWMMXT @@ -6323,45 +6311,42 @@ void qInitDrawhelperAsm() } #endif // IWMMXT -#if defined(QT_COMPILER_SUPPORTS_NEON) && !defined(Q_OS_IOS) - if (features & NEON) { - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; - qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon; - qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon; - qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon; +#if defined(__ARM_NEON__) && !defined(Q_OS_IOS) + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon; #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon; - qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon; - qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon; #endif - qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon; - qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon; + qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon; + qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon; - qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon; - qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon; + qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon; + qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon; - qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon; + qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon; - functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon; - functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon; - functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon; - destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon; - destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon; + functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon; + functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon; + functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon; + destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon; + destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon; - qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon; - qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon; - qt_memfill32 = qt_memfill32_neon; + qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon; + qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon; - extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length); + extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length); - qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon; - } + qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon; #endif #if defined(QT_COMPILER_SUPPORTS_MIPS_DSP) @@ -6385,8 +6370,6 @@ void qInitDrawhelperAsm() functionForModeSolid_C[QPainter::CompositionMode_Xor] = comp_func_solid_XOR_mips_dsp; functionForModeSolid_C[QPainter::CompositionMode_SourceOut] = comp_func_solid_SourceOut_mips_dsp; - qt_memfill32 = qt_memfill32_asm_mips_dsp; - qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp; qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mips_dsp; @@ -6427,16 +6410,4 @@ void qInitDrawhelperAsm() functionForMode = functionForModeAsm; } -static void qt_memfill32_setup(quint32 *dest, quint32 value, int count) -{ - qInitDrawhelperAsm(); - qt_memfill32(dest, value, count); -} - -static void qt_memfill16_setup(quint16 *dest, quint16 value, int count) -{ - qInitDrawhelperAsm(); - qt_memfill16(dest, value, count); -} - QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index 541b3ef619..a40166d5be 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -43,7 +43,7 @@ #include <private/qblendfunctions_p.h> #include <private/qmath_p.h> -#ifdef QT_COMPILER_SUPPORTS_NEON +#ifdef __ARM_NEON__ #include <private/qdrawhelper_neon_p.h> #include <private/qpaintengine_raster_p.h> @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE -void qt_memfill32_neon(quint32 *dest, quint32 value, int count) +void qt_memfill32(quint32 *dest, quint32 value, int count) { const int epilogueSize = count % 16; if (count >= 16) { @@ -998,5 +998,5 @@ const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Opera QT_END_NAMESPACE -#endif // QT_COMPILER_SUPPORTS_NEON +#endif // __ARM_NEON__ diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h index 475df639f8..cad6fe22e9 100644 --- a/src/gui/painting/qdrawhelper_neon_p.h +++ b/src/gui/painting/qdrawhelper_neon_p.h @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE -#ifdef QT_COMPILER_SUPPORTS_NEON +#ifdef __ARM_NEON__ void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, @@ -139,7 +139,7 @@ void QT_FASTCALL qt_destStoreRGB16_neon(QRasterBuffer *rasterBuffer, void QT_FASTCALL comp_func_solid_SourceOver_neon(uint *destPixels, int length, uint color, uint const_alpha); void QT_FASTCALL comp_func_Plus_neon(uint *dst, const uint *src, int length, uint const_alpha); -#endif // QT_COMPILER_SUPPORTS_NEON +#endif // __ARM_NEON__ QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 418294c56d..3c945338a6 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -171,6 +171,8 @@ extern MemRotateFunc qMemRotateFunctions[QImage::NImageFormats][3]; extern DrawHelper qDrawHelper[QImage::NImageFormats]; void qBlendTexture(int count, const QSpan *spans, void *userData); +extern void qt_memfill32(quint32 *dest, quint32 value, int count); +extern void qt_memfill16(quint16 *dest, quint16 value, int count); typedef void (QT_FASTCALL *CompositionFunction)(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha); @@ -386,8 +388,6 @@ static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c) return (b * b) - (4 * a * c); } -extern void (*qt_memfill32)(quint32 *dest, quint32 value, int count); - template <class RadialFetchFunc> Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length) @@ -691,12 +691,16 @@ static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16_32(uint x, uint a) { return t; } -#define INV_PREMUL(p) \ - (qAlpha(p) == 0 ? 0 : \ - ((qAlpha(p) << 24) \ - | (((255*qRed(p))/ qAlpha(p)) << 16) \ - | (((255*qGreen(p)) / qAlpha(p)) << 8) \ - | ((255*qBlue(p)) / qAlpha(p)))) +static Q_ALWAYS_INLINE uint INV_PREMUL(uint p) { + const uint alpha = qAlpha(p); + if (alpha == 255) + return p; + if (alpha == 0) + return 0; + // (p*(0x00ff00ff/alpha)) >> 16 == (p*255)/alpha for all p and alpha <= 256. + const uint invAlpha = 0x00ff00ffU / alpha; + return qRgba((qRed(p)*invAlpha)>>16, (qGreen(p)*invAlpha)>>16, (qBlue(p)*invAlpha)>>16, alpha); +} struct quint24 { quint24(uint value); @@ -726,7 +730,6 @@ template<> inline void qt_memfill(quint32 *dest, quint32 color, int count) template<> inline void qt_memfill(quint16 *dest, quint16 color, int count) { - extern void (*qt_memfill16)(quint16 *dest, quint16 value, int count); qt_memfill16(dest, color, count); } @@ -1031,6 +1034,7 @@ struct QPixelLayout BPP bpp; ConvertFunc convertToARGB32PM; ConvertFunc convertFromARGB32PM; + ConvertFunc convertFromRGB32; }; typedef const uint *(QT_FASTCALL *FetchPixelsFunc)(uint *buffer, const uchar *src, int index, int count); diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index a9dc5a7fb7..d11ba0b26c 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -238,7 +238,7 @@ void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, u } } -void qt_memfill32_sse2(quint32 *dest, quint32 value, int count) +void qt_memfill32(quint32 *dest, quint32 value, int count) { if (count < 7) { switch (count) { @@ -285,7 +285,7 @@ void qt_memfill32_sse2(quint32 *dest, quint32 value, int count) void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha) { if ((const_alpha & qAlpha(color)) == 255) { - qt_memfill32_sse2(destPixels, color, length); + qt_memfill32(destPixels, color, length); } else { if (const_alpha != 255) color = BYTE_MUL(color, const_alpha); @@ -397,7 +397,7 @@ CompositionFunction qt_functionForMode_SSE2[numCompositionFunctions] = { }; #endif -void qt_memfill16_sse2(quint16 *dest, quint16 value, int count) +void qt_memfill16(quint16 *dest, quint16 value, int count) { if (count < 3) { switch (count) { @@ -413,7 +413,7 @@ void qt_memfill16_sse2(quint16 *dest, quint16 value, int count) } const quint32 value32 = (value << 16) | value; - qt_memfill32_sse2(reinterpret_cast<quint32*>(dest), value32, count / 2); + qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2); if (count & 0x1) dest[count - 1] = value; diff --git a/src/gui/painting/qdrawhelper_x86_p.h b/src/gui/painting/qdrawhelper_x86_p.h index d64b9cec39..699c586cb0 100644 --- a/src/gui/painting/qdrawhelper_x86_p.h +++ b/src/gui/painting/qdrawhelper_x86_p.h @@ -57,9 +57,9 @@ QT_BEGIN_NAMESPACE -#ifdef QT_COMPILER_SUPPORTS_SSE2 -void qt_memfill32_sse2(quint32 *dest, quint32 value, int count); -void qt_memfill16_sse2(quint16 *dest, quint16 value, int count); +#ifdef __SSE2__ +void qt_memfill32(quint32 *dest, quint32 value, int count); +void qt_memfill16(quint16 *dest, quint16 value, int count); void qt_bitmapblit32_sse2(QRasterBuffer *rasterBuffer, int x, int y, quint32 color, const uchar *src, int width, int height, int stride); @@ -77,26 +77,7 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, extern CompositionFunction qt_functionForMode_SSE2[]; extern CompositionFunctionSolid qt_functionForModeSolid_SSE2[]; -#endif // QT_COMPILER_SUPPORTS_SSE2 - -#ifdef QT_COMPILER_SUPPORTS_AVX -void qt_memfill32_avx(quint32 *dest, quint32 value, int count); -void qt_memfill16_avx(quint16 *dest, quint16 value, int count); -void qt_bitmapblit32_avx(QRasterBuffer *rasterBuffer, int x, int y, - quint32 color, - const uchar *src, int width, int height, int stride); -void qt_bitmapblit16_avx(QRasterBuffer *rasterBuffer, int x, int y, - quint32 color, - const uchar *src, int width, int height, int stride); -void qt_blend_argb32_on_argb32_avx(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); -void qt_blend_rgb32_on_rgb32_avx(uchar *destPixels, int dbpl, - const uchar *srcPixels, int sbpl, - int w, int h, - int const_alpha); -#endif // QT_COMPILER_SUPPORTS_AVX +#endif // __SSE2__ #ifdef QT_COMPILER_SUPPORTS_IWMMXT void qt_blend_color_argb_iwmmxt(int count, const QSpan *spans, void *userData); diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h index 0e0c06f56c..cdf68b932d 100644 --- a/src/gui/painting/qdrawingprimitive_sse2_p.h +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -44,7 +44,7 @@ #include <private/qsimd_p.h> -#ifdef QT_COMPILER_SUPPORTS_SSE2 +#ifdef __SSE2__ // // W A R N I N G @@ -242,6 +242,6 @@ QT_BEGIN_NAMESPACE QT_END_NAMESPACE -#endif // QT_COMPILER_SUPPORTS_SSE2 +#endif // __SSE2__ #endif // QDRAWINGPRIMITIVE_SSE2_P_H diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index f1eaea0f6b..acab08e794 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -387,6 +387,7 @@ void QPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDraw \value OpenGL2 \value PaintBuffer \value Blitter + \value Direct2D Windows only, Direct2D based engine */ /*! diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 18b6d84146..7b928ba5f6 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -207,6 +207,7 @@ public: OpenGL2, PaintBuffer, Blitter, + Direct2D, User = 50, // first user type id MaxUser = 100 // last user type id diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 156e411154..aa2b9bea54 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1738,8 +1738,8 @@ QList<QPolygonF> QPainterPath::toFillPolygons(const QMatrix &matrix) const //same as qt_polygon_isect_line in qpolygon.cpp static void qt_painterpath_isect_line(const QPointF &p1, - const QPointF &p2, - const QPointF &pos, + const QPointF &p2, + const QPointF &pos, int *winding) { qreal x1 = p1.x(); @@ -2551,6 +2551,26 @@ QPainterPathStroker::QPainterPathStroker() } /*! + Creates a new stroker based on \a pen. + + \since 5.3 + */ +QPainterPathStroker::QPainterPathStroker(const QPen &pen) + : d_ptr(new QPainterPathStrokerPrivate) +{ + setWidth(pen.widthF()); + setCapStyle(pen.capStyle()); + setJoinStyle(pen.joinStyle()); + setMiterLimit(pen.miterLimit()); + setDashOffset(pen.dashOffset()); + + if (pen.style() == Qt::CustomDashLine) + setDashPattern(pen.dashPattern()); + else + setDashPattern(pen.style()); +} + +/*! Destroys the stroker. */ QPainterPathStroker::~QPainterPathStroker() diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index e22c1729f3..c922867eb9 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -57,6 +57,7 @@ class QPainterPathPrivate; struct QPainterPathPrivateDeleter; class QPainterPathData; class QPainterPathStrokerPrivate; +class QPen; class QPolygonF; class QRegion; class QVectorPath; @@ -243,6 +244,7 @@ class Q_GUI_EXPORT QPainterPathStroker Q_DECLARE_PRIVATE(QPainterPathStroker) public: QPainterPathStroker(); + QPainterPathStroker(const QPen &pen); ~QPainterPathStroker(); void setWidth(qreal width); diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index 6a3eacd67a..c0b3769c2d 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -455,15 +455,19 @@ QVector<qreal> QPen::dashPattern() const switch (d->style) { case Qt::DashLine: + dd->dashPattern.reserve(2); dd->dashPattern << dash << space; break; case Qt::DotLine: + dd->dashPattern.reserve(2); dd->dashPattern << dot << space; break; case Qt::DashDotLine: + dd->dashPattern.reserve(4); dd->dashPattern << dash << space << dot << space; break; case Qt::DashDotDotLine: + dd->dashPattern.reserve(6); dd->dashPattern << dash << space << dot << space << dot << space; break; default: diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 42cf15ee3b..22d2585898 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -716,7 +716,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, FT_Set_Transform(face, &matrix, 0); freetype->matrix = matrix; // fake bold - if ((fontDef.weight == QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) + if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face)) embolden = true; // underline metrics line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale)); @@ -1187,7 +1187,7 @@ int QFontEngineFT::synthesized() const int s = 0; if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)) s = SynthesizedItalic; - if ((fontDef.weight == QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) + if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD)) s |= SynthesizedBold; if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face)) s |= SynthesizedStretch; diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp index d12f3cccd8..ac9762b183 100644 --- a/src/gui/text/qtextcursor.cpp +++ b/src/gui/text/qtextcursor.cpp @@ -174,7 +174,6 @@ void QTextCursorPrivate::remove() } else { priv->remove(pos1, pos2-pos1, op); adjusted_anchor = anchor = position; - priv->finishEdit(); } } diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 4a34f0d3c3..fa54776b6d 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -551,6 +551,39 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option) } /*! + \property QTextDocument::baseUrl + \since 5.3 + \brief the base URL used to resolve relative resource URLs within the document. + + Resource URLs are resolved to be within the same directory as the target of the base + URL meaning any portion of the path after the last '/' will be ignored. + + \table + \header \li Base URL \li Relative URL \li Resolved URL + \row \li file:///path/to/content \li images/logo.png \li file:///path/to/images/logo.png + \row \li file:///path/to/content/ \li images/logo.png \li file:///path/to/content/images/logo.png + \row \li file:///path/to/content/index.html \li images/logo.png \li file:///path/to/content/images/logo.png + \row \li file:///path/to/content/images/ \li ../images/logo.png \li file:///path/to/content/images/logo.png + \endtable +*/ +QUrl QTextDocument::baseUrl() const +{ + Q_D(const QTextDocument); + return d->baseUrl; +} + +void QTextDocument::setBaseUrl(const QUrl &url) +{ + Q_D(QTextDocument); + if (d->baseUrl != url) { + d->baseUrl = url; + if (d->lout) + d->lout->documentChanged(0, 0, d->length()); + emit baseUrlChanged(url); + } +} + +/*! \since 4.8 The default cursor movement style is used by all QTextCursor objects @@ -1849,11 +1882,12 @@ void QTextDocument::print(QPagedPaintDevice *printer) const QVariant QTextDocument::resource(int type, const QUrl &name) const { Q_D(const QTextDocument); - QVariant r = d->resources.value(name); + const QUrl url = d->baseUrl.resolved(name); + QVariant r = d->resources.value(url); if (!r.isValid()) { - r = d->cachedResources.value(name); + r = d->cachedResources.value(url); if (!r.isValid()) - r = const_cast<QTextDocument *>(this)->loadResource(type, name); + r = const_cast<QTextDocument *>(this)->loadResource(type, url); } return r; } @@ -1924,27 +1958,29 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name) } // if resource was not loaded try to load it here - if (!qobject_cast<QTextDocument *>(p) && r.isNull() && name.isRelative()) { - QUrl currentURL = d->url; + if (!qobject_cast<QTextDocument *>(p) && r.isNull()) { QUrl resourceUrl = name; - // For the second case QUrl can merge "#someanchor" with "foo.html" - // correctly to "foo.html#someanchor" - if (!(currentURL.isRelative() - || (currentURL.scheme() == QLatin1String("file") - && !QFileInfo(currentURL.toLocalFile()).isAbsolute())) - || (name.hasFragment() && name.path().isEmpty())) { - resourceUrl = currentURL.resolved(name); - } else { - // this is our last resort when current url and new url are both relative - // we try to resolve against the current working directory in the local - // file system. - QFileInfo fi(currentURL.toLocalFile()); - if (fi.exists()) { - resourceUrl = - QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name); - } else if (currentURL.isEmpty()) { - resourceUrl.setScheme(QLatin1String("file")); + if (name.isRelative()) { + QUrl currentURL = d->url; + // For the second case QUrl can merge "#someanchor" with "foo.html" + // correctly to "foo.html#someanchor" + if (!(currentURL.isRelative() + || (currentURL.scheme() == QLatin1String("file") + && !QFileInfo(currentURL.toLocalFile()).isAbsolute())) + || (name.hasFragment() && name.path().isEmpty())) { + resourceUrl = currentURL.resolved(name); + } else { + // this is our last resort when current url and new url are both relative + // we try to resolve against the current working directory in the local + // file system. + QFileInfo fi(currentURL.toLocalFile()); + if (fi.exists()) { + resourceUrl = + QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name); + } else if (currentURL.isEmpty()) { + resourceUrl.setScheme(QLatin1String("file")); + } } } @@ -2124,13 +2160,21 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) html += QLatin1String("pt;"); attributesEmitted = true; } else if (format.hasProperty(QTextFormat::FontSizeAdjustment)) { - static const char * const sizeNames[] = { - "small", "medium", "large", "x-large", "xx-large" + static const char sizeNameData[] = + "small" "\0" + "medium" "\0" + "xx-large" ; + static const quint8 sizeNameOffsets[] = { + 0, // "small" + sizeof("small"), // "medium" + sizeof("small") + sizeof("medium") + 3, // "large" ) + sizeof("small") + sizeof("medium") + 1, // "x-large" )> compressed into "xx-large" + sizeof("small") + sizeof("medium"), // "xx-large" ) }; const char *name = 0; const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1; if (idx >= 0 && idx <= 4) { - name = sizeNames[idx]; + name = sizeNameData + sizeNameOffsets[idx]; } if (name) { html += QLatin1String(" font-size:"); diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index d8f52e9f98..854cb29ed9 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -47,6 +47,7 @@ #include <QtCore/qrect.h> #include <QtCore/qvariant.h> #include <QtGui/qfont.h> +#include <QtCore/qurl.h> QT_BEGIN_NAMESPACE @@ -63,7 +64,6 @@ class QTextFormat; class QTextFrame; class QTextBlock; class QTextCodec; -class QUrl; class QVariant; class QRectF; class QTextOption; @@ -116,6 +116,7 @@ class Q_GUI_EXPORT QTextDocument : public QObject Q_PROPERTY(int maximumBlockCount READ maximumBlockCount WRITE setMaximumBlockCount) Q_PROPERTY(qreal documentMargin READ documentMargin WRITE setDocumentMargin) QDOC_PROPERTY(QTextOption defaultTextOption READ defaultTextOption WRITE setDefaultTextOption) + Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged) public: explicit QTextDocument(QObject *parent = 0); @@ -258,6 +259,9 @@ public: QTextOption defaultTextOption() const; void setDefaultTextOption(const QTextOption &option); + QUrl baseUrl() const; + void setBaseUrl(const QUrl &url); + Qt::CursorMoveStyle defaultCursorMoveStyle() const; void setDefaultCursorMoveStyle(Qt::CursorMoveStyle style); @@ -270,7 +274,7 @@ Q_SIGNALS: void modificationChanged(bool m); void cursorPositionChanged(const QTextCursor &cursor); void blockCountChanged(int newBlockCount); - + void baseUrlChanged(const QUrl &url); void documentLayoutChanged(); public Q_SLOTS: diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index 8d4cab30ae..fa22131c9e 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -355,6 +355,7 @@ public: QString url; qreal indentWidth; qreal documentMargin; + QUrl baseUrl; void mergeCachedResources(const QTextDocumentPrivate *priv); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 06c5e24920..109b7e600f 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -241,7 +241,8 @@ using namespace std; static const char *directions[] = { "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON", - "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN" + "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN", + "DirLRI", "DirRLI", "DirFSI", "DirPDI" }; #endif @@ -2536,7 +2537,8 @@ static inline bool nextCharJoins(const QString &string, int pos) ++pos; if (pos == string.length()) return false; - return string.at(pos).joining() != QChar::OtherJoining; + // ### U+A872 has joining type L + return string.at(pos) == QChar(0xA872) || string.at(pos).joining() != QChar::OtherJoining; } static inline bool prevCharJoins(const QString &string, int pos) @@ -2551,13 +2553,9 @@ static inline bool prevCharJoins(const QString &string, int pos) static inline bool isRetainableControlCode(QChar c) { - return (c.unicode() == 0x202a // LRE - || c.unicode() == 0x202b // LRE - || c.unicode() == 0x202c // PDF - || c.unicode() == 0x202d // LRO - || c.unicode() == 0x202e // RLO - || c.unicode() == 0x200e // LRM - || c.unicode() == 0x200f); // RLM + return (c.unicode() >= 0x202a && c.unicode() <= 0x202e) // LRE, RLE, PDF, LRO, RLO + || (c.unicode() >= 0x200e && c.unicode() <= 0x200f) // LRM, RLM + || (c.unicode() >= 0x2066 && c.unicode() <= 0x2069); // LRM, RLM } static QString stringMidRetainingBidiCC(const QString &string, diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index fb71ab40b8..f2e16cf546 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -449,7 +449,6 @@ public: typedef QList<ItemDecoration> ItemDecorationList; - QTextEngine(LayoutData *data); QTextEngine(); QTextEngine(const QString &str, const QFont &f); ~QTextEngine(); diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index 2389427da0..641a2ceb8a 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -706,6 +706,15 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) */ /*! + \fn bool QTextFormat::isEmpty() const + \since 5.3 + + Returns true if the format does not store any properties; false otherwise. + + \sa propertyCount(), properties() +*/ + +/*! \fn bool QTextFormat::isCharFormat() const Returns \c true if this text format is a \c CharFormat; otherwise @@ -3376,19 +3385,6 @@ bool QTextFormatCollection::hasFormatCached(const QTextFormat &format) const return false; } -QTextFormat QTextFormatCollection::objectFormat(int objectIndex) const -{ - if (objectIndex == -1) - return QTextFormat(); - return format(objFormats.at(objectIndex)); -} - -void QTextFormatCollection::setObjectFormat(int objectIndex, const QTextFormat &f) -{ - const int formatIndex = indexForFormat(f); - objFormats[objectIndex] = formatIndex; -} - int QTextFormatCollection::objectFormatIndex(int objectIndex) const { if (objectIndex == -1) diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index 2098369811..c04ac3876b 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -295,6 +295,7 @@ public: void merge(const QTextFormat &other); inline bool isValid() const { return type() != InvalidFormat; } + inline bool isEmpty() const { return propertyCount() == 0; } int type() const; diff --git a/src/gui/text/qtextformat_p.h b/src/gui/text/qtextformat_p.h index 6b2958a4b6..e3998d4f3f 100644 --- a/src/gui/text/qtextformat_p.h +++ b/src/gui/text/qtextformat_p.h @@ -68,8 +68,10 @@ public: QTextFormatCollection(const QTextFormatCollection &rhs); QTextFormatCollection &operator=(const QTextFormatCollection &rhs); - QTextFormat objectFormat(int objectIndex) const; - void setObjectFormat(int objectIndex, const QTextFormat &format); + inline QTextFormat objectFormat(int objectIndex) const + { return format(objectFormatIndex(objectIndex)); } + inline void setObjectFormat(int objectIndex, const QTextFormat &format) + { setObjectFormatIndex(objectIndex, indexForFormat(format)); } int objectFormatIndex(int objectIndex) const; void setObjectFormatIndex(int objectIndex, int formatIndex); diff --git a/src/gui/image/qimage_avx.cpp b/src/gui/util/qabstractlayoutstyleinfo.cpp index d04ec5b3de..4f7c635594 100644 --- a/src/gui/image/qimage_avx.cpp +++ b/src/gui/util/qabstractlayoutstyleinfo.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2012 Intel Corporation +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -39,19 +39,15 @@ ** ****************************************************************************/ -#include <private/qsimd_p.h> +#include "qabstractlayoutstyleinfo_p.h" -#ifdef QT_COMPILER_SUPPORTS_AVX +QT_BEGIN_NAMESPACE -#ifndef __AVX__ -#error "AVX not enabled in this file, cannot proceed" -#endif +bool QAbstractLayoutStyleInfo::hasChanged() const +{ + if (m_changed == Unknown) + m_changed = hasChangedCore() ? Changed : Unchanged; + return m_changed == Changed; +} -#define convert_ARGB_to_ARGB_PM_inplace_sse2 convert_ARGB_to_ARGB_PM_inplace_avx -#include "qimage_sse2.cpp" - -#define qt_convert_rgb888_to_rgb32_ssse3 qt_convert_rgb888_to_rgb32_avx -#define convert_RGB888_to_RGB32_ssse3 convert_RGB888_to_RGB32_avx -#include "qimage_ssse3.cpp" - -#endif +QT_END_NAMESPACE diff --git a/src/gui/util/qabstractlayoutstyleinfo_p.h b/src/gui/util/qabstractlayoutstyleinfo_p.h new file mode 100644 index 0000000000..52f151c5d2 --- /dev/null +++ b/src/gui/util/qabstractlayoutstyleinfo_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTLAYOUTSTYLEINFO_P_H +#define QABSTRACTLAYOUTSTYLEINFO_P_H + +#include <QtCore/qnamespace.h> +#include "qlayoutpolicy_p.h" + +QT_BEGIN_NAMESPACE + + +class Q_GUI_EXPORT QAbstractLayoutStyleInfo { +public: + typedef enum { + Unknown = 0, + Changed, + Unchanged + } ChangedState; + + QAbstractLayoutStyleInfo() : m_isWindow(false), m_changed(Changed) {} + virtual ~QAbstractLayoutStyleInfo() {} + virtual qreal combinedLayoutSpacing(QLayoutPolicy::ControlTypes /*controls1*/, + QLayoutPolicy::ControlTypes /*controls2*/, Qt::Orientation /*orientation*/) const { + return -1; + } + + virtual qreal perItemSpacing(QLayoutPolicy::ControlType /*control1*/, + QLayoutPolicy::ControlType /*control2*/, + Qt::Orientation /*orientation*/) const { + return -1; + } + + virtual qreal spacing(Qt::Orientation orientation) const = 0; + + virtual bool hasChangedCore() const = 0; + + void updateChanged(ChangedState change) { + m_changed = change; + } + + bool hasChanged() const; + + virtual void invalidate() { updateChanged(Changed);} + + virtual qreal windowMargin(Qt::Orientation orientation) const = 0; + + bool isWindow() const { + return m_isWindow; + } + +protected: + unsigned m_isWindow : 1; + mutable unsigned m_changed : 2; +}; + +QT_END_NAMESPACE + +#endif // QABSTRACTLAYOUTSTYLEINFO_P_H diff --git a/src/gui/util/qgridlayoutengine.cpp b/src/gui/util/qgridlayoutengine.cpp new file mode 100644 index 0000000000..be38e92f21 --- /dev/null +++ b/src/gui/util/qgridlayoutengine.cpp @@ -0,0 +1,1636 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include <math.h> + +#include "qgridlayoutengine_p.h" +#include "qvarlengtharray.h" + +#include <QtDebug> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +template <typename T> +static void insertOrRemoveItems(QVector<T> &items, int index, int delta) +{ + int count = items.count(); + if (index < count) { + if (delta > 0) { + items.insert(index, delta, T()); + } else if (delta < 0) { + items.remove(index, qMin(-delta, count - index)); + } + } +} + +static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired) +{ + Q_ASSERT(sumDesired != 0.0); + return desired * qPow(sumAvailable / sumDesired, desired / sumDesired); +} + +static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize) +{ + if (descent < 0.0) + return -1.0; + + Q_ASSERT(descent >= 0.0); + Q_ASSERT(ascent >= 0.0); + Q_ASSERT(targetSize >= ascent + descent); + + qreal extra = targetSize - (ascent + descent); + return descent + (extra / 2.0); +} + +static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which) +{ + qreal size1 = box1.q_sizes(which); + qreal size2 = box2.q_sizes(which); + + if (which == MaximumSize) { + return size2 - size1; + } else { + return size1 - size2; + } +} + +void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing) +{ + Q_ASSERT(q_minimumDescent < 0.0); + + q_minimumSize += other.q_minimumSize + spacing; + q_preferredSize += other.q_preferredSize + spacing; + q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing; +} + +void QGridLayoutBox::combine(const QGridLayoutBox &other) +{ + q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent); + q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent); + + q_minimumSize = qMax(q_minimumAscent + q_minimumDescent, + qMax(q_minimumSize, other.q_minimumSize)); + qreal maxMax; + if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX) + maxMax = other.q_maximumSize; + else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX) + maxMax = q_maximumSize; + else + maxMax = qMax(q_maximumSize, other.q_maximumSize); + + q_maximumSize = qMax(q_minimumSize, maxMax); + q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize), + q_maximumSize); +} + +void QGridLayoutBox::normalize() +{ + q_maximumSize = qMax(qreal(0.0), q_maximumSize); + q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize); + q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize); + q_minimumDescent = qMin(q_minimumDescent, q_minimumSize); + + Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0)); +} + +#ifdef QGRIDLAYOUTENGINE_DEBUG +void QGridLayoutBox::dump(int indent) const +{ + qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize, + q_maximumSize, q_minimumAscent, q_minimumDescent); +} +#endif + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2) +{ + for (int i = 0; i < NSizes; ++i) { + if (box1.q_sizes(i) != box2.q_sizes(i)) + return false; + } + return box1.q_minimumDescent == box2.q_minimumDescent + && box1.q_minimumAscent == box2.q_minimumAscent; +} + +void QGridLayoutRowData::reset(int count) +{ + ignore.fill(false, count); + boxes.fill(QGridLayoutBox(), count); + multiCellMap.clear(); + stretches.fill(0, count); + spacings.fill(0.0, count); + hasIgnoreFlag = false; +} + +void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo) +{ + MultiCellMap::const_iterator i = multiCellMap.constBegin(); + for (; i != multiCellMap.constEnd(); ++i) { + int start = i.key().first; + int span = i.key().second; + int end = start + span; + const QGridLayoutBox &box = i.value().q_box; + int stretch = i.value().q_stretch; + + QGridLayoutBox totalBox = this->totalBox(start, end); + QVarLengthArray<QGridLayoutBox> extras(span); + QVarLengthArray<qreal> dummy(span); + QVarLengthArray<qreal> newSizes(span); + + for (int j = 0; j < NSizes; ++j) { + qreal extra = compare(box, totalBox, j); + if (extra > 0.0) { + calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(), + 0, totalBox, rowInfo); + + for (int k = 0; k < span; ++k) + extras[k].q_sizes(j) = newSizes[k]; + } + } + + for (int k = 0; k < span; ++k) { + boxes[start + k].combine(extras[k]); + if (stretch != 0) + stretches[start + k] = qMax(stretches[start + k], stretch); + } + } + multiCellMap.clear(); +} + +void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions, + qreal *sizes, qreal *descents, + const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo) +{ + Q_ASSERT(end > start); + + targetSize = qMax(totalBox.q_minimumSize, targetSize); + + int n = end - start; + QVarLengthArray<qreal> newSizes(n); + QVarLengthArray<qreal> factors(n); + qreal sumFactors = 0.0; + int sumStretches = 0; + qreal sumAvailable; + + for (int i = 0; i < n; ++i) { + if (stretches[start + i] > 0) + sumStretches += stretches[start + i]; + } + + if (targetSize < totalBox.q_preferredSize) { + stealBox(start, end, MinimumSize, positions, sizes); + + sumAvailable = targetSize - totalBox.q_minimumSize; + if (sumAvailable > 0.0) { + qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal desired = box.q_preferredSize - box.q_minimumSize; + factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired); + sumFactors += factors[i]; + } + + for (int i = 0; i < n; ++i) { + Q_ASSERT(sumFactors > 0.0); + qreal delta = sumAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } else { + bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize); + if (isLargerThanMaximum) { + stealBox(start, end, MaximumSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_maximumSize; + } else { + stealBox(start, end, PreferredSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_preferredSize; + } + + if (sumAvailable > 0.0) { + qreal sumCurrentAvailable = sumAvailable; + bool somethingHasAMaximumSize = false; + + qreal sumSizes = 0.0; + for (int i = 0; i < n; ++i) + sumSizes += sizes[i]; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + newSizes[i] = 0.0; + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal boxSize; + + qreal desired; + if (isLargerThanMaximum) { + boxSize = box.q_maximumSize; + desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize; + } else { + boxSize = box.q_preferredSize; + desired = box.q_maximumSize - boxSize; + } + if (desired == 0.0) { + newSizes[i] = sizes[i]; + factors[i] = 0.0; + } else { + Q_ASSERT(desired > 0.0); + + int stretch = stretches[start + i]; + if (sumStretches == 0) { + if (hasIgnoreFlag || sizes[i] == 0.0) { + factors[i] = (stretch < 0) ? 1.0 : 0.0; + } else { + factors[i] = (stretch < 0) ? sizes[i] : 0.0; + } + } else if (stretch == sumStretches) { + factors[i] = 1.0; + } else if (stretch <= 0) { + factors[i] = 0.0; + } else { + qreal ultimateSize; + qreal ultimateSumSizes; + qreal x = ((stretch * sumSizes) + - (sumStretches * boxSize)) + / (sumStretches - stretch); + if (x >= 0.0) { + ultimateSize = boxSize + x; + ultimateSumSizes = sumSizes + x; + } else { + ultimateSize = boxSize; + ultimateSumSizes = (sumStretches * boxSize) + / stretch; + } + + /* + We multiply these by 1.5 to give some space for a smooth transition + (at the expense of the stretch factors, which are not fully respected + during the transition). + */ + ultimateSize = ultimateSize * 3 / 2; + ultimateSumSizes = ultimateSumSizes * 3 / 2; + + qreal beta = ultimateSumSizes - sumSizes; + if (!beta) { + factors[i] = 1; + } else { + qreal alpha = qMin(sumCurrentAvailable, beta); + qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches) + - (boxSize); + qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta; + + factors[i] = ((alpha * ultimateFactor) + + ((beta - alpha) * transitionalFactor)) / beta; + } + + } + sumFactors += factors[i]; + if (desired < sumCurrentAvailable) + somethingHasAMaximumSize = true; + + newSizes[i] = -1.0; + } + } + + bool keepGoing = somethingHasAMaximumSize; + while (keepGoing) { + keepGoing = false; + + for (int i = 0; i < n; ++i) { + if (newSizes[i] >= 0.0) + continue; + + qreal maxBoxSize; + if (isLargerThanMaximum) + maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize; + else + maxBoxSize = boxes.at(start + i).q_maximumSize; + + qreal avail = sumCurrentAvailable * factors[i] / sumFactors; + if (sizes[i] + avail >= maxBoxSize) { + newSizes[i] = maxBoxSize; + sumCurrentAvailable -= maxBoxSize - sizes[i]; + sumFactors -= factors[i]; + keepGoing = (sumCurrentAvailable > 0.0); + if (!keepGoing) + break; + } + } + } + + for (int i = 0; i < n; ++i) { + if (newSizes[i] < 0.0) { + qreal delta = (sumFactors == 0.0) ? 0.0 + : sumCurrentAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } + } + + if (sumAvailable > 0) { + qreal offset = 0; + for (int i = 0; i < n; ++i) { + qreal delta = newSizes[i] - sizes[i]; + positions[i] += offset; + sizes[i] += delta; + offset += delta; + } + +#if 0 // some "pixel allocation" + int surplus = targetSize - (positions[n - 1] + sizes[n - 1]); + Q_ASSERT(surplus >= 0 && surplus <= n); + + int prevSurplus = -1; + while (surplus > 0 && surplus != prevSurplus) { + prevSurplus = surplus; + + int offset = 0; + for (int i = 0; i < n; ++i) { + const QGridLayoutBox &box = boxes.at(start + i); + int delta = (!ignore.testBit(start + i) && surplus > 0 + && factors[i] > 0 && sizes[i] < box.q_maximumSize) + ? 1 : 0; + + positions[i] += offset; + sizes[i] += delta; + offset += delta; + surplus -= delta; + } + } + Q_ASSERT(surplus == 0); +#endif + } + + if (descents) { + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) + continue; + const QGridLayoutBox &box = boxes.at(start + i); + descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]); + } + } +} + +QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const +{ + QGridLayoutBox result; + if (start < end) { + result.q_maximumSize = 0.0; + qreal nextSpacing = 0.0; + for (int i = start; i < end; ++i) { + if (ignore.testBit(i)) + continue; + result.add(boxes.at(i), stretches.at(i), nextSpacing); + nextSpacing = spacings.at(i); + } + } + return result; +} + +void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes) +{ + qreal offset = 0.0; + qreal nextSpacing = 0.0; + + for (int i = start; i < end; ++i) { + qreal avail = 0.0; + + if (!ignore.testBit(i)) { + const QGridLayoutBox &box = boxes.at(i); + avail = box.q_sizes(which); + offset += nextSpacing; + nextSpacing = spacings.at(i); + } + + *positions++ = offset; + *sizes++ = avail; + offset += avail; + } +} + +#ifdef QGRIDLAYOUTENGINE_DEBUG +void QGridLayoutRowData::dump(int indent) const +{ + qDebug("%*sData", indent, ""); + + for (int i = 0; i < ignore.count(); ++i) { + qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i), + spacings.at(i)); + if (ignore.testBit(i)) + qDebug("%*s Ignored", indent, ""); + boxes.at(i).dump(indent + 2); + } + + MultiCellMap::const_iterator it = multiCellMap.constBegin(); + while (it != multiCellMap.constEnd()) { + qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first, + it.key().second, it.value().q_stretch); + it.value().q_box.dump(indent + 2); + } +} +#endif + +QGridLayoutItem::QGridLayoutItem(int row, int column, int rowSpan, int columnSpan, + Qt::Alignment alignment) + : q_alignment(alignment) +{ + q_firstRows[Hor] = column; + q_firstRows[Ver] = row; + q_rowSpans[Hor] = columnSpan; + q_rowSpans[Ver] = rowSpan; + q_stretches[Hor] = -1; + q_stretches[Ver] = -1; +} + +int QGridLayoutItem::firstRow(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Horizontal]; +} + +int QGridLayoutItem::lastRow(Qt::Orientation orientation) const +{ + return firstRow(orientation) + rowSpan(orientation) - 1; +} + +int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const +{ + return firstColumn(orientation) + columnSpan(orientation) - 1; +} + +int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Horizontal]; +} + +void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation) +{ + q_firstRows[orientation == Qt::Vertical] = row; +} + +void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation) +{ + q_rowSpans[orientation == Qt::Vertical] = rowSpan; +} + +int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const +{ + int stretch = q_stretches[orientation == Qt::Vertical]; + if (stretch >= 0) + return stretch; + + QLayoutPolicy::Policy policy = sizePolicy(orientation); + + if (policy & QLayoutPolicy::ExpandFlag) { + return 1; + } else if (policy & QLayoutPolicy::GrowFlag) { + return -1; // because we max it up + } else { + return 0; + } +} + +void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(stretch >= 0); // ### deal with too big stretches + q_stretches[orientation == Qt::Vertical] = stretch; +} + +QLayoutPolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /*side*/) const +{ + return QLayoutPolicy::DefaultType; +} + +QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const +{ + QGridLayoutBox result; + QLayoutPolicy::Policy policy = sizePolicy(orientation); + + if (orientation == Qt::Horizontal) { + QSizeF constraintSize(-1.0, constraint); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width(); + + if (policy & QLayoutPolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + } else { + QSizeF constraintSize(constraint, -1.0); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height(); + + if (policy & QLayoutPolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + + if (alignment() & Qt::AlignBaseline) { + result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height(); + if (result.q_minimumDescent != -1.0) { + const qreal minSizeHint = sizeHint(Qt::MinimumSize, constraintSize).height(); + result.q_minimumDescent -= (minSizeHint - result.q_minimumSize); + result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent; + } + } + } + if (policy & QLayoutPolicy::IgnoreFlag) + result.q_preferredSize = result.q_minimumSize; + + return result; +} + +QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height, + qreal rowDescent, Qt::Alignment align) const +{ + QGridLayoutBox vBox = box(Qt::Vertical); + if (!(align & Qt::AlignBaseline) || vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) { + qreal cellWidth = width; + qreal cellHeight = height; + + + QSizeF size = effectiveMaxSize(QSizeF(-1,-1)); + if (hasDynamicConstraint()) { + if (dynamicConstraintOrientation() == Qt::Vertical) { + if (size.width() > cellWidth) + size = effectiveMaxSize(QSizeF(cellWidth, -1)); + } else if (size.height() > cellHeight) { + size = effectiveMaxSize(QSizeF(-1, cellHeight)); + } + } + size = size.boundedTo(QSizeF(cellWidth, cellHeight)); + width = size.width(); + height = size.height(); + + switch (align & Qt::AlignHorizontal_Mask) { + case Qt::AlignHCenter: + x += (cellWidth - width)/2; + break; + case Qt::AlignRight: + x += cellWidth - width; + break; + default: + break; + } + switch (align & Qt::AlignVertical_Mask) { + case Qt::AlignVCenter: + y += (cellHeight - height)/2; + break; + case Qt::AlignBottom: + y += cellHeight - height; + break; + default: + break; + } + return QRectF(x, y, width, height); + } else { + width = qMin(effectiveMaxSize(QSizeF(-1,-1)).width(), width); + qreal descent = vBox.q_minimumDescent; + qreal ascent = vBox.q_minimumSize - descent; + return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent); + } +} + +void QGridLayoutItem::transpose() +{ + qSwap(q_firstRows[Hor], q_firstRows[Ver]); + qSwap(q_rowSpans[Hor], q_rowSpans[Ver]); + qSwap(q_stretches[Hor], q_stretches[Ver]); +} + +void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldFirstRow = firstRow(orientation); + if (oldFirstRow >= row) { + setFirstRow(oldFirstRow + delta, orientation); + } else if (lastRow(orientation) >= row) { + setRowSpan(rowSpan(orientation) + delta, orientation); + } +} +/*! + \internal + returns the effective maximumSize, will take the sizepolicy into + consideration. (i.e. if sizepolicy does not have QLayoutPolicy::Grow, then + maxSizeHint will be the preferredSize) + Note that effectiveSizeHint does not take sizePolicy into consideration, + (since it only evaluates the hints, as the name implies) +*/ +QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const +{ + QSizeF size = constraint; + bool vGrow = (sizePolicy(Qt::Vertical) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag; + bool hGrow = (sizePolicy(Qt::Horizontal) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag; + if (!vGrow || !hGrow) { + QSizeF pref = sizeHint(Qt::PreferredSize, constraint); + if (!vGrow) + size.setHeight(pref.height()); + if (!hGrow) + size.setWidth(pref.width()); + } + + if (!size.isValid()) { + QSizeF maxSize = sizeHint(Qt::MaximumSize, size); + if (size.width() == -1) + size.setWidth(maxSize.width()); + if (size.height() == -1) + size.setHeight(maxSize.height()); + } + return size; +} + +#ifdef QGRIDLAYOUTENGINE_DEBUG +void QGridLayoutItem::dump(int indent) const +{ + qDebug("%*s (%d, %d) %d x %d", indent, "", firstRow(), firstColumn(), //### + rowSpan(), columnSpan()); + + if (q_stretches[Hor] >= 0) + qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]); + if (q_stretches[Ver] >= 0) + qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]); + if (q_alignment != 0) + qDebug("%*s Alignment: %x", indent, "", uint(q_alignment)); + qDebug("%*s Horizontal size policy: %x Vertical size policy: %x", + indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical)); +} +#endif + +void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta) +{ + count += delta; + + insertOrRemoveItems(stretches, row, delta); + insertOrRemoveItems(spacings, row, delta); + insertOrRemoveItems(alignments, row, delta); + insertOrRemoveItems(boxes, row, delta); +} + +#ifdef QGRIDLAYOUTENGINE_DEBUG +void QGridLayoutRowInfo::dump(int indent) const +{ + qDebug("%*sInfo (count: %d)", indent, "", count); + for (int i = 0; i < count; ++i) { + QString message; + + if (stretches.value(i).value() >= 0) + message += QString::fromLatin1(" stretch %1").arg(stretches.value(i).value()); + if (spacings.value(i).value() >= 0.0) + message += QString::fromLatin1(" spacing %1").arg(spacings.value(i).value()); + if (alignments.value(i) != 0) + message += QString::fromLatin1(" alignment %1").arg(int(alignments.value(i)), 16); + + if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) { + qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message)); + if (boxes.value(i) != QGridLayoutBox()) + boxes.value(i).dump(indent + 1); + } + } +} +#endif + +QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment) +{ + m_visualDirection = Qt::LeftToRight; + m_defaultAlignment = defaultAlignment; + invalidate(); +} + +int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].count; +} + +int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Horizontal].count; +} + +int QGridLayoutEngine::itemCount() const +{ + return q_items.count(); +} + +QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const +{ + Q_ASSERT(index >= 0 && index < itemCount()); + return q_items.at(index); +} + +int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveFirstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveLastRows[orientation == Qt::Vertical]; +} + +void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations) +{ + Q_ASSERT(spacing >= 0.0); + if (orientations & Qt::Horizontal) + q_defaultSpacings[Hor].setUserValue(spacing); + if (orientations & Qt::Vertical) + q_defaultSpacings[Ver].setUserValue(spacing); + + invalidate(); +} + +qreal QGridLayoutEngine::spacing(Qt::Orientation orientation, const QAbstractLayoutStyleInfo *styleInfo) const +{ + if (!q_defaultSpacings[orientation == Qt::Vertical].isUser()) { + qreal defaultSpacing = styleInfo->spacing(orientation); + q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing); + } + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.spacings.count()) + rowInfo.spacings.resize(row + 1); + if (spacing >= 0) + rowInfo.spacings[row].setUserValue(spacing); + else + rowInfo.spacings[row] = QLayoutParameter<qreal>(); + invalidate(); +} + +qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const +{ + QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row); + if (!spacing.isDefault()) + return spacing.value(); + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(stretch >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.stretches.count()) + rowInfo.stretches.resize(row + 1); + rowInfo.stretches[row].setUserValue(stretch); +} + +int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const +{ + QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row); + if (!stretch.isDefault()) + return stretch.value(); + return 0; +} + +void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(size >= 0.0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.boxes.count()) + rowInfo.boxes.resize(row + 1); + rowInfo.boxes[row].q_sizes(which) = size; +} + +qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which); +} + +void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.alignments.count()) + rowInfo.alignments.resize(row + 1); + rowInfo.alignments[row] = alignment; +} + +Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const +{ + Q_ASSERT(row >= 0); + return q_infos[orientation == Qt::Vertical].alignments.value(row); +} + +Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const +{ + Qt::Alignment align = layoutItem->alignment(); + if (!(align & Qt::AlignVertical_Mask)) { + // no vertical alignment, respect the row alignment + int y = layoutItem->firstRow(); + align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask); + if (!(align & Qt::AlignVertical_Mask)) + align |= (m_defaultAlignment & Qt::AlignVertical_Mask); + } + if (!(align & Qt::AlignHorizontal_Mask)) { + // no horizontal alignment, respect the column alignment + int x = layoutItem->firstColumn(); + align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask); + } + + return align; +} + +/*! + \internal + The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order + of visual arrangement. Strictly speaking it does not have to, but most people expect it to. + (And if it didn't we would have to add itemArrangedAt(int index) or something..) + */ +void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index) +{ + maybeExpandGrid(item->lastRow(), item->lastColumn()); + + if (index == -1) + q_items.append(item); + else + q_items.insert(index, item); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j)) + qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j); + setItemAt(i, j, item); + } + } +} + +void QGridLayoutEngine::addItem(QGridLayoutItem *item) +{ + insertItem(item, -1); +} + +void QGridLayoutEngine::removeItem(QGridLayoutItem *item) +{ + Q_ASSERT(q_items.contains(item)); + + invalidate(); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j) == item) + setItemAt(i, j, 0); + } + } + + q_items.removeAll(item); +} + + +QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const +{ + if (orientation == Qt::Horizontal) + qSwap(row, column); + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())) + return 0; + return q_grid.at((row * internalGridColumnCount()) + column); +} + +void QGridLayoutEngine::invalidate() +{ + q_cachedEffectiveFirstRows[Hor] = -1; + q_cachedEffectiveFirstRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + q_cachedEffectiveLastRows[Ver] = -1; + q_totalBoxesValid = false; + q_sizeHintValid[Hor] = false; + q_sizeHintValid[Ver] = false; + q_cachedSize = QSizeF(); + q_cachedConstraintOrientation = UnknownConstraint; +} + +static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect) +{ + if (dir == Qt::RightToLeft) + geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left())); +} + +void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo) +{ + if (rowCount() < 1 || columnCount() < 1) + return; + + ensureGeometries(contentsGeometry.size(), styleInfo); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + qreal x = q_xx[item->firstColumn()]; + qreal y = q_yy[item->firstRow()]; + qreal width = q_widths[item->lastColumn()]; + qreal height = q_heights[item->lastRow()]; + + if (item->columnSpan() != 1) + width += q_xx[item->lastColumn()] - x; + if (item->rowSpan() != 1) + height += q_yy[item->lastRow()] - y; + + QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y, + width, height, q_descents[item->lastRow()], effectiveAlignment(item)); + visualRect(&geom, visualDirection(), contentsGeometry); + item->setGeometry(geom); + } +} + +// ### candidate for deletion +QRectF QGridLayoutEngine::cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, + int columnSpan, const QAbstractLayoutStyleInfo *styleInfo) const +{ + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()) + || rowSpan < 1 || columnSpan < 1) + return QRectF(); + + ensureGeometries(contentsGeometry.size(), styleInfo); + + int lastColumn = qMax(column + columnSpan, columnCount()) - 1; + int lastRow = qMax(row + rowSpan, rowCount()) - 1; + + qreal x = q_xx[column]; + qreal y = q_yy[row]; + qreal width = q_widths[lastColumn]; + qreal height = q_heights[lastRow]; + + if (columnSpan != 1) + width += q_xx[lastColumn] - x; + if (rowSpan != 1) + height += q_yy[lastRow] - y; + + return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height); +} + +QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which, const QSizeF &constraint, + const QAbstractLayoutStyleInfo *styleInfo) const +{ + + + if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) { + QGridLayoutBox sizehint_totalBoxes[NOrientations]; + bool sizeHintCalculated = false; + if (constraintOrientation() == Qt::Vertical) { + //We have items whose height depends on their width + if (constraint.width() >= 0) { + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + QVector<qreal> sizehint_xx; + QVector<qreal> sizehint_widths; + + sizehint_xx.resize(columnCount()); + sizehint_widths.resize(columnCount()); + qreal width = constraint.width(); + //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as + //constraints to find the row heights + q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(), + 0, sizehint_totalBoxes[Hor], q_infos[Hor]); + ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical, styleInfo); + sizeHintCalculated = true; + } + } else { + if (constraint.height() >= 0) { + //We have items whose width depends on their height + ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + QVector<qreal> sizehint_yy; + QVector<qreal> sizehint_heights; + + sizehint_yy.resize(rowCount()); + sizehint_heights.resize(rowCount()); + qreal height = constraint.height(); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as + //constraints to find the column widths + q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(), + 0, sizehint_totalBoxes[Ver], q_infos[Ver]); + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal, styleInfo); + sizeHintCalculated = true; + } + } + if (sizeHintCalculated) + return QSizeF(sizehint_totalBoxes[Hor].q_sizes(which), sizehint_totalBoxes[Ver].q_sizes(which)); + } + + //No items with height for width, so it doesn't matter which order we do these in + ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + return QSizeF(q_totalBoxes[Hor].q_sizes(which), q_totalBoxes[Ver].q_sizes(which)); +} + +QLayoutPolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const +{ + Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal; + int row = (side == Top || side == Left) ? effectiveFirstRow(orientation) + : effectiveLastRow(orientation); + QLayoutPolicy::ControlTypes result = 0; + + for (int column = columnCount(orientation) - 1; column >= 0; --column) { + if (QGridLayoutItem *item = itemAt(row, column, orientation)) + result |= item->controlTypes(side); + } + return result; +} + +void QGridLayoutEngine::transpose() +{ + invalidate(); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->transpose(); + + qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]); + qSwap(q_infos[Hor], q_infos[Ver]); + + regenerateGrid(); +} + +void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction) +{ + m_visualDirection = direction; +} + +Qt::LayoutDirection QGridLayoutEngine::visualDirection() const +{ + return m_visualDirection; +} + +#ifdef QGRIDLAYOUTENGINE_DEBUG +void QGridLayoutEngine::dump(int indent) const +{ + qDebug("%*sEngine", indent, ""); + + qDebug("%*s Items (%d)", indent, "", q_items.count()); + int i; + for (i = 0; i < q_items.count(); ++i) + q_items.at(i)->dump(indent + 2); + + qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(), + internalGridColumnCount()); + for (int row = 0; row < internalGridRowCount(); ++row) { + QString message = QLatin1String("[ "); + for (int column = 0; column < internalGridColumnCount(); ++column) { + message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3); + message += QLatin1Char(' '); + } + message += QLatin1Char(']'); + qDebug("%*s %s", indent, "", qPrintable(message)); + } + + if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0) + qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(), + q_defaultSpacings[Ver].value()); + + qDebug("%*s Column and row info", indent, ""); + q_infos[Hor].dump(indent + 2); + q_infos[Ver].dump(indent + 2); + + qDebug("%*s Column and row data", indent, ""); + q_columnData.dump(indent + 2); + q_rowData.dump(indent + 2); + + qDebug("%*s Geometries output", indent, ""); + QVector<qreal> *cellPos = &q_yy; + for (int pass = 0; pass < 2; ++pass) { + QString message; + for (i = 0; i < cellPos->count(); ++i) { + message += QLatin1String((message.isEmpty() ? "[" : ", ")); + message += QString::number(cellPos->at(i)); + } + message += QLatin1Char(']'); + qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message)); + cellPos = &q_xx; + } +} +#endif + +void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation) +{ + invalidate(); // ### move out of here? + + if (orientation == Qt::Horizontal) + qSwap(row, column); + + if (row < rowCount() && column < columnCount()) + return; + + int oldGridRowCount = internalGridRowCount(); + int oldGridColumnCount = internalGridColumnCount(); + + q_infos[Ver].count = qMax(row + 1, rowCount()); + q_infos[Hor].count = qMax(column + 1, columnCount()); + + int newGridRowCount = internalGridRowCount(); + int newGridColumnCount = internalGridColumnCount(); + + int newGridSize = newGridRowCount * newGridColumnCount; + if (newGridSize != q_grid.count()) { + q_grid.resize(newGridSize); + + if (newGridColumnCount != oldGridColumnCount) { + for (int i = oldGridRowCount - 1; i >= 1; --i) { + for (int j = oldGridColumnCount - 1; j >= 0; --j) { + int oldIndex = (i * oldGridColumnCount) + j; + int newIndex = (i * newGridColumnCount) + j; + + Q_ASSERT(newIndex > oldIndex); + q_grid[newIndex] = q_grid[oldIndex]; + q_grid[oldIndex] = 0; + } + } + } + } +} + +void QGridLayoutEngine::regenerateGrid() +{ + q_grid.fill(0); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + for (int j = item->firstRow(); j <= item->lastRow(); ++j) { + for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) { + setItemAt(j, k, item); + } + } + } +} + +void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item) +{ + Q_ASSERT(row >= 0 && row < rowCount()); + Q_ASSERT(column >= 0 && column < columnCount()); + q_grid[(row * internalGridColumnCount()) + column] = item; +} + +void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldRowCount = rowCount(orientation); + Q_ASSERT(uint(row) <= uint(oldRowCount)); + + invalidate(); + + // appending rows (or columns) is easy + if (row == oldRowCount && delta > 0) { + maybeExpandGrid(oldRowCount + delta - 1, -1, orientation); + return; + } + + q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->insertOrRemoveRows(row, delta, orientation); + + q_grid.resize(internalGridRowCount() * internalGridColumnCount()); + regenerateGrid(); +} + +void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, + const qreal *colPositions, const qreal *colSizes, + Qt::Orientation orientation, + const QAbstractLayoutStyleInfo *styleInfo) const +{ + const int ButtonMask = QLayoutPolicy::ButtonBox | QLayoutPolicy::PushButton; + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal]; + LayoutSide top = (orientation == Qt::Vertical) ? Top : Left; + LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right; + + const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical]; + qreal innerSpacing = styleInfo->spacing(orientation); + if (innerSpacing >= 0.0) + defaultSpacing.setCachedValue(innerSpacing); + + for (int row = 0; row < rowInfo.count; ++row) { + bool rowIsEmpty = true; + bool rowIsIdenticalToPrevious = (row > 0); + + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + + if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation)) + rowIsIdenticalToPrevious = false; + + if (item) + rowIsEmpty = false; + } + + if ((rowIsEmpty || rowIsIdenticalToPrevious) + && rowInfo.spacings.value(row).isDefault() + && rowInfo.stretches.value(row).isDefault() + && rowInfo.boxes.value(row) == QGridLayoutBox()) + rowData->ignore.setBit(row, true); + + if (rowInfo.spacings.value(row).isUser()) { + rowData->spacings[row] = rowInfo.spacings.at(row).value(); + } else if (!defaultSpacing.isDefault()) { + rowData->spacings[row] = defaultSpacing.value(); + } + + rowData->stretches[row] = rowInfo.stretches.value(row).value(); + } + + struct RowAdHocData { + int q_row; + unsigned int q_hasButtons : 8; + unsigned int q_hasNonButtons : 8; + + inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {} + inline void init(int row) { + this->q_row = row; + q_hasButtons = false; + q_hasNonButtons = false; + } + inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; } + inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; } + }; + RowAdHocData lastRowAdHocData; + RowAdHocData nextToLastRowAdHocData; + RowAdHocData nextToNextToLastRowAdHocData; + + rowData->hasIgnoreFlag = false; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + QGridLayoutBox &rowBox = rowData->boxes[row]; + if (styleInfo->isWindow()) { + nextToNextToLastRowAdHocData = nextToLastRowAdHocData; + nextToLastRowAdHocData = lastRowAdHocData; + lastRowAdHocData.init(row); + } + + bool userRowStretch = rowInfo.stretches.value(row).isUser(); + int &rowStretch = rowData->stretches[row]; + + bool hasIgnoreFlag = true; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + if (item) { + int itemRow = item->firstRow(orientation); + int itemColumn = item->firstColumn(orientation); + + if (itemRow == row && itemColumn == column) { + int itemStretch = item->stretchFactor(orientation); + if (!(item->sizePolicy(orientation) & QLayoutPolicy::IgnoreFlag)) + hasIgnoreFlag = false; + int itemRowSpan = item->rowSpan(orientation); + + int effectiveRowSpan = 1; + for (int i = 1; i < itemRowSpan; ++i) { + if (!rowData->ignore.testBit(i + itemRow)) + ++effectiveRowSpan; + } + + QGridLayoutBox *box; + if (effectiveRowSpan == 1) { + box = &rowBox; + if (!userRowStretch && itemStretch != 0) + rowStretch = qMax(rowStretch, itemStretch); + } else { + QGridLayoutMultiCellData &multiCell = + rowData->multiCellMap[qMakePair(row, effectiveRowSpan)]; + box = &multiCell.q_box; + multiCell.q_stretch = itemStretch; + } + // Items with constraints need to be passed the constraint + if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) { + /* Get the width of the item by summing up the widths of the columns that it spans. + * We need to have already calculated the widths of the columns by calling + * q_columns->calculateGeometries() before hand and passing the value in the colSizes + * and colPositions parameters. + * The variable name is still colSizes even when it actually has the row sizes + */ + qreal length = colSizes[item->lastColumn(orientation)]; + if (item->columnSpan(orientation) != 1) + length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)]; + box->combine(item->box(orientation, length)); + } else { + box->combine(item->box(orientation)); + } + + if (effectiveRowSpan == 1) { + QLayoutPolicy::ControlTypes controls = item->controlTypes(top); + if (controls & ButtonMask) + lastRowAdHocData.q_hasButtons = true; + if (controls & ~ButtonMask) + lastRowAdHocData.q_hasNonButtons = true; + } + } + } + } + if (row < rowInfo.boxes.count()) { + QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row); + rowBoxInfo.normalize(); + rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize); + rowBox.q_maximumSize = qMax(rowBox.q_minimumSize, + (rowBoxInfo.q_maximumSize != FLT_MAX ? + rowBoxInfo.q_maximumSize : rowBox.q_maximumSize)); + rowBox.q_preferredSize = qBound(rowBox.q_minimumSize, + qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize), + rowBox.q_maximumSize); + } + if (hasIgnoreFlag) + rowData->hasIgnoreFlag = true; + } + + /* + Heuristic: Detect button boxes that don't use QLayoutPolicy::ButtonBox. + This is somewhat ad hoc but it usually does the trick. + */ + bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyNonButtons()); + bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyButtons() + && nextToNextToLastRowAdHocData.hasOnlyNonButtons() + && orientation == Qt::Vertical); + + if (defaultSpacing.isDefault()) { + int prevRow = -1; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) { + qreal &rowSpacing = rowData->spacings[prevRow]; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item1 = itemAt(prevRow, column, orientation); + QGridLayoutItem *item2 = itemAt(row, column, orientation); + + if (item1 && item2 && item1 != item2) { + QLayoutPolicy::ControlTypes controls1 = item1->controlTypes(bottom); + QLayoutPolicy::ControlTypes controls2 = item2->controlTypes(top); + + if (controls2 & QLayoutPolicy::PushButton) { + if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox) + || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) { + controls2 &= ~QLayoutPolicy::PushButton; + controls2 |= QLayoutPolicy::ButtonBox; + } + } + + qreal spacing = styleInfo->combinedLayoutSpacing(controls1, controls2, + orientation); + if (orientation == Qt::Horizontal) { + qreal width1 = rowData->boxes.at(prevRow).q_minimumSize; + qreal width2 = rowData->boxes.at(row).q_minimumSize; + QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0, effectiveAlignment(item1)); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0, effectiveAlignment(item2)); + spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x(); + } else { + const QGridLayoutBox &box1 = rowData->boxes.at(prevRow); + const QGridLayoutBox &box2 = rowData->boxes.at(row); + qreal height1 = box1.q_minimumSize; + qreal height2 = box2.q_minimumSize; + qreal rowDescent1 = fixedDescent(box1.q_minimumDescent, + box1.q_minimumAscent, height1); + qreal rowDescent2 = fixedDescent(box2.q_minimumDescent, + box2.q_minimumAscent, height2); + QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1, + rowDescent1, effectiveAlignment(item1)); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2, + rowDescent2, effectiveAlignment(item2)); + spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y(); + } + rowSpacing = qMax(spacing, rowSpacing); + } + } + } + prevRow = row; + } + } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) { + /* + Even for styles that define a uniform spacing, we cheat a + bit and use the window margin as the spacing. This + significantly improves the look of dialogs. + */ + int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row + : nextToNextToLastRowAdHocData.q_row; + if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) { + qreal windowMargin = styleInfo->windowMargin(orientation); + qreal &rowSpacing = rowData->spacings[prevRow]; + rowSpacing = qMax(windowMargin, rowSpacing); + } + } +} + +void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const +{ + if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) { + int rowCount = this->rowCount(); + int columnCount = this->columnCount(); + + q_cachedEffectiveFirstRows[Ver] = rowCount; + q_cachedEffectiveFirstRows[Hor] = columnCount; + q_cachedEffectiveLastRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + + for (int i = q_items.count() - 1; i >= 0; --i) { + const QGridLayoutItem *item = q_items.at(i); + + for (int j = 0; j < NOrientations; ++j) { + Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical; + if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j]) + q_cachedEffectiveFirstRows[j] = item->firstRow(orientation); + if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j]) + q_cachedEffectiveLastRows[j] = item->lastRow(orientation); + } + } + } +} + +void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox, + const qreal *colPositions, const qreal *colSizes, + Qt::Orientation orientation, + const QAbstractLayoutStyleInfo *styleInfo) const +{ + const int o = (orientation == Qt::Vertical ? Ver : Hor); + if (q_sizeHintValid[o] && !colPositions && !colSizes) { + if (totalBox != &q_totalBoxes[o]) + *totalBox = q_totalBoxes[o]; + return; + } + rowData->reset(rowCount(orientation)); + fillRowData(rowData, colPositions, colSizes, orientation, styleInfo); + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + rowData->distributeMultiCells(rowInfo); + *totalBox = rowData->totalBox(0, rowCount(orientation)); + + if (!colPositions && !colSizes) { + q_totalBoxes[o] = *totalBox; + q_sizeHintValid[o] = true; + } +} + +/** + returns false if the layout has contradicting constraints (i.e. some items with a horizontal + constraint and other items with a vertical constraint) + */ +bool QGridLayoutEngine::ensureDynamicConstraint() const +{ + if (q_cachedConstraintOrientation == UnknownConstraint) { + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + if (item->hasDynamicConstraint()) { + Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation(); + if (q_cachedConstraintOrientation == UnknownConstraint) { + q_cachedConstraintOrientation = itemConstraintOrientation; + } else if (q_cachedConstraintOrientation != itemConstraintOrientation) { + q_cachedConstraintOrientation = UnfeasibleConstraint; + qWarning("QGridLayoutEngine: Unfeasible, cannot mix horizontal and" + " vertical constraint in the same layout"); + return false; + } + } + } + if (q_cachedConstraintOrientation == UnknownConstraint) + q_cachedConstraintOrientation = NoConstraint; + } + return true; +} + +bool QGridLayoutEngine::hasDynamicConstraint() const +{ + if (!ensureDynamicConstraint()) + return false; + return q_cachedConstraintOrientation != NoConstraint; +} + +/* + * return value is only valid if hasConstraint() returns \c true + */ +Qt::Orientation QGridLayoutEngine::constraintOrientation() const +{ + (void)ensureDynamicConstraint(); + return (Qt::Orientation)q_cachedConstraintOrientation; +} + +void QGridLayoutEngine::ensureGeometries(const QSizeF &size, + const QAbstractLayoutStyleInfo *styleInfo) const +{ + if (!styleInfo->hasChanged() && q_totalBoxesValid && q_cachedSize == size) + return; + + q_totalBoxesValid = true; + q_cachedSize = size; + + q_xx.resize(columnCount()); + q_widths.resize(columnCount()); + q_yy.resize(rowCount()); + q_heights.resize(rowCount()); + q_descents.resize(rowCount()); + + if (constraintOrientation() != Qt::Horizontal) { + //We might have items whose width depends on their height + ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as + //constraints to find the row heights + q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), + 0, q_totalBoxes[Hor], q_infos[Hor] ); + ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], q_xx.data(), q_widths.data(), Qt::Vertical, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() + q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); + } else { + //We have items whose height depends on their width + ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as + //constraints to find the column widths + q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); + ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], q_yy.data(), q_heights.data(), Qt::Horizontal, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() + q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), + 0, q_totalBoxes[Hor], q_infos[Hor]); + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/util/qgridlayoutengine_p.h b/src/gui/util/qgridlayoutengine_p.h new file mode 100644 index 0000000000..9650e7fffe --- /dev/null +++ b/src/gui/util/qgridlayoutengine_p.h @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRIDLAYOUTENGINE_P_H +#define QGRIDLAYOUTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the graphics view layout classes. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qalgorithms.h" +#include "qbitarray.h" +#include "qlist.h" +#include "qmap.h" +#include "qpair.h" +#include <QtCore/qvector.h> +#include <QtCore/qsize.h> +#include <QtCore/qrect.h> +#include <float.h> +#include "qlayoutpolicy_p.h" +#include "qabstractlayoutstyleinfo_p.h" + +// #define QGRIDLAYOUTENGINE_DEBUG + +QT_BEGIN_NAMESPACE + +class QStyle; +class QWidget; + +// ### deal with Descent in a similar way +enum { + MinimumSize = Qt::MinimumSize, + PreferredSize = Qt::PreferredSize, + MaximumSize = Qt::MaximumSize, + NSizes +}; + +// do not reorder +enum { + Hor, + Ver, + NOrientations +}; + +// do not reorder +enum LayoutSide { + Left, + Top, + Right, + Bottom +}; + +enum { + NoConstraint, + HorizontalConstraint, // Width depends on the height + VerticalConstraint, // Height depends on the width + UnknownConstraint, // need to update cache + UnfeasibleConstraint // not feasible, it be has some items with Vertical and others with Horizontal constraints +}; + +template <typename T> +class QLayoutParameter +{ +public: + enum State { Default, User, Cached }; + + inline QLayoutParameter() : q_value(T()), q_state(Default) {} + inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {} + + inline void setUserValue(T value) { + q_value = value; + q_state = User; + } + inline void setCachedValue(T value) const { + if (q_state != User) { + q_value = value; + q_state = Cached; + } + } + inline T value() const { return q_value; } + inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; } + inline bool isDefault() const { return q_state == Default; } + inline bool isUser() const { return q_state == User; } + inline bool isCached() const { return q_state == Cached; } + +private: + mutable T q_value; + mutable State q_state; +}; + +class QStretchParameter : public QLayoutParameter<int> +{ +public: + QStretchParameter() : QLayoutParameter<int>(-1) {} + +}; + +class Q_GUI_EXPORT QGridLayoutBox +{ +public: + inline QGridLayoutBox() + : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX), + q_minimumDescent(-1), q_minimumAscent(-1) {} + + void add(const QGridLayoutBox &other, int stretch, qreal spacing); + void combine(const QGridLayoutBox &other); + void normalize(); + +#ifdef QGRIDLAYOUTENGINE_DEBUG + void dump(int indent = 0) const; +#endif + // This code could use the union-struct-array trick, but a compiler + // bug prevents this from working. + qreal q_minimumSize; + qreal q_preferredSize; + qreal q_maximumSize; + qreal q_minimumDescent; + qreal q_minimumAscent; + inline qreal &q_sizes(int which) + { + qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } + inline const qreal &q_sizes(int which) const + { + const qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } +}; + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2); +inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2) + { return !operator==(box1, box2); } + +class QGridLayoutMultiCellData +{ +public: + inline QGridLayoutMultiCellData() : q_stretch(-1) {} + + QGridLayoutBox q_box; + int q_stretch; +}; + +typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap; + +class QGridLayoutRowInfo; + +class QGridLayoutRowData +{ +public: + void reset(int count); + void distributeMultiCells(const QGridLayoutRowInfo &rowInfo); + void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, + qreal *descents, const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo); + QGridLayoutBox totalBox(int start, int end) const; + void stealBox(int start, int end, int which, qreal *positions, qreal *sizes); + +#ifdef QGRIDLAYOUTENGINE_DEBUG + void dump(int indent = 0) const; +#endif + + QBitArray ignore; // ### rename q_ + QVector<QGridLayoutBox> boxes; + MultiCellMap multiCellMap; + QVector<int> stretches; + QVector<qreal> spacings; + bool hasIgnoreFlag; +}; + +class QGridLayoutRowInfo +{ +public: + inline QGridLayoutRowInfo() : count(0) {} + + void insertOrRemoveRows(int row, int delta); + +#ifdef QGRIDLAYOUTENGINE_DEBUG + void dump(int indent = 0) const; +#endif + + int count; + QVector<QStretchParameter> stretches; + QVector<QLayoutParameter<qreal> > spacings; + QVector<Qt::Alignment> alignments; + QVector<QGridLayoutBox> boxes; +}; + + +class Q_GUI_EXPORT QGridLayoutItem +{ +public: + QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1, + Qt::Alignment alignment = 0); + virtual ~QGridLayoutItem() {} + + inline int firstRow() const { return q_firstRows[Ver]; } + inline int firstColumn() const { return q_firstRows[Hor]; } + inline int rowSpan() const { return q_rowSpans[Ver]; } + inline int columnSpan() const { return q_rowSpans[Hor]; } + inline int lastRow() const { return firstRow() + rowSpan() - 1; } + inline int lastColumn() const { return firstColumn() + columnSpan() - 1; } + + int firstRow(Qt::Orientation orientation) const; + int firstColumn(Qt::Orientation orientation) const; + int lastRow(Qt::Orientation orientation) const; + int lastColumn(Qt::Orientation orientation) const; + int rowSpan(Qt::Orientation orientation) const; + int columnSpan(Qt::Orientation orientation) const; + void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical); + void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical); + + int stretchFactor(Qt::Orientation orientation) const; + void setStretchFactor(int stretch, Qt::Orientation orientation); + + inline Qt::Alignment alignment() const { return q_alignment; } + inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; } + + virtual QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const = 0; + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0; + + virtual void setGeometry(const QRectF &rect) = 0; + /* + returns true if the size policy returns true for either hasHeightForWidth() + or hasWidthForHeight() + */ + virtual bool hasDynamicConstraint() const { return false; } + virtual Qt::Orientation dynamicConstraintOrientation() const { return Qt::Horizontal; } + + + virtual QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; + + QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent, Qt::Alignment align) const; + QGridLayoutBox box(Qt::Orientation orientation, qreal constraint = -1.0) const; + + + void transpose(); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + QSizeF effectiveMaxSize(const QSizeF &constraint) const; + +#ifdef QGRIDLAYOUTENGINE_DEBUG + void dump(int indent = 0) const; +#endif + +private: + int q_firstRows[NOrientations]; + int q_rowSpans[NOrientations]; + int q_stretches[NOrientations]; + Qt::Alignment q_alignment; + +}; + +class Q_GUI_EXPORT QGridLayoutEngine +{ +public: + QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(0)); + inline ~QGridLayoutEngine() { qDeleteAll(q_items); } + + int rowCount(Qt::Orientation orientation) const; + int columnCount(Qt::Orientation orientation) const; + inline int rowCount() const { return q_infos[Ver].count; } + inline int columnCount() const { return q_infos[Hor].count; } + // returns the number of items inserted, which may be less than (rowCount * columnCount) + int itemCount() const; + QGridLayoutItem *itemAt(int index) const; + + int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const; + int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const; + + void setSpacing(qreal spacing, Qt::Orientations orientations); + qreal spacing(Qt::Orientation orientation, const QAbstractLayoutStyleInfo *styleInfo) const; + // ### setSpacingAfterRow(), spacingAfterRow() + void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical); + qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const; + + void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical); + int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const; + + void setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation = Qt::Vertical); + qreal rowSizeHint(Qt::SizeHint which, int row, + Qt::Orientation orientation = Qt::Vertical) const; + + void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation); + Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const; + + Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const; + + + void insertItem(QGridLayoutItem *item, int index); + void addItem(QGridLayoutItem *item); + void removeItem(QGridLayoutItem *item); + void deleteItems() + { + const QList<QGridLayoutItem *> oldItems = q_items; + q_items.clear(); // q_items are used as input when the grid is regenerated in removeRows + // The following calls to removeRows are suboptimal + int rows = rowCount(Qt::Vertical); + removeRows(0, rows, Qt::Vertical); + rows = rowCount(Qt::Horizontal); + removeRows(0, rows, Qt::Horizontal); + qDeleteAll(oldItems); + } + + QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const; + inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical) + { insertOrRemoveRows(row, +1, orientation); } + inline void removeRows(int row, int count, Qt::Orientation orientation) + { insertOrRemoveRows(row, -count, orientation); } + + void invalidate(); + void setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo); + QRectF cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, int columnSpan, + const QAbstractLayoutStyleInfo *styleInfo) const; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint, + const QAbstractLayoutStyleInfo *styleInfo) const; + + // heightForWidth / widthForHeight support + QSizeF dynamicallyConstrainedSizeHint(Qt::SizeHint which, const QSizeF &constraint) const; + bool ensureDynamicConstraint() const; + bool hasDynamicConstraint() const; + Qt::Orientation constraintOrientation() const; + + + QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; + void transpose(); + void setVisualDirection(Qt::LayoutDirection direction); + Qt::LayoutDirection visualDirection() const; +#ifdef QGRIDLAYOUTENGINE_DEBUG + void dump(int indent = 0) const; +#endif + +private: + static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; } + + void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical); + void regenerateGrid(); + inline int internalGridRowCount() const { return grossRoundUp(rowCount()); } + inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); } + void setItemAt(int row, int column, QGridLayoutItem *item); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + void fillRowData(QGridLayoutRowData *rowData, + const qreal *colPositions, const qreal *colSizes, + Qt::Orientation orientation, + const QAbstractLayoutStyleInfo *styleInfo) const; + void ensureEffectiveFirstAndLastRows() const; + void ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox, + const qreal *colPositions, const qreal *colSizes, + Qt::Orientation orientation, + const QAbstractLayoutStyleInfo *styleInfo) const; + + void ensureGeometries(const QSizeF &size, const QAbstractLayoutStyleInfo *styleInfo) const; +protected: + QList<QGridLayoutItem *> q_items; +private: + // User input + QVector<QGridLayoutItem *> q_grid; + QLayoutParameter<qreal> q_defaultSpacings[NOrientations]; + QGridLayoutRowInfo q_infos[NOrientations]; + Qt::LayoutDirection m_visualDirection; + Qt::Alignment m_defaultAlignment; + + // Lazily computed from the above user input + mutable int q_cachedEffectiveFirstRows[NOrientations]; + mutable int q_cachedEffectiveLastRows[NOrientations]; + mutable quint8 q_cachedConstraintOrientation : 3; + + // Layout item input + mutable QGridLayoutRowData q_columnData; + mutable QGridLayoutRowData q_rowData; + mutable QGridLayoutBox q_totalBoxes[NOrientations]; + + // Output + mutable QSizeF q_cachedSize; + mutable bool q_totalBoxesValid; + mutable bool q_sizeHintValid[NOrientations]; + mutable QVector<qreal> q_xx; + mutable QVector<qreal> q_yy; + mutable QVector<qreal> q_widths; + mutable QVector<qreal> q_heights; + mutable QVector<qreal> q_descents; + + friend class QGridLayoutItem; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/util/qlayoutpolicy.cpp b/src/gui/util/qlayoutpolicy.cpp new file mode 100644 index 0000000000..9a154768eb --- /dev/null +++ b/src/gui/util/qlayoutpolicy.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlayoutpolicy_p.h" +#include <QtCore/qdebug.h> +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +void QLayoutPolicy::setControlType(ControlType type) +{ + /* + The control type is a flag type, with values 0x1, 0x2, 0x4, 0x8, 0x10, + etc. In memory, we pack it onto the available bits (CTSize) in + setControlType(), and unpack it here. + + Example: + + 0x00000001 maps to 0 + 0x00000002 maps to 1 + 0x00000004 maps to 2 + 0x00000008 maps to 3 + etc. + */ + + int i = 0; + while (true) { + if (type & (0x1 << i)) { + bits.ctype = i; + return; + } + ++i; + } +} + +QLayoutPolicy::ControlType QLayoutPolicy::controlType() const +{ + return QLayoutPolicy::ControlType(1 << bits.ctype); +} + +#ifndef QT_NO_DATASTREAM + +/*! + \relates QLayoutPolicy + + Writes the size \a policy to the data stream \a stream. + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ +QDataStream &operator<<(QDataStream &stream, const QLayoutPolicy &policy) +{ + // The order here is for historical reasons. (compatibility with Qt4) + quint32 data = (policy.bits.horPolicy | // [0, 3] + policy.bits.verPolicy << 4 | // [4, 7] + policy.bits.hfw << 8 | // [8] + policy.bits.ctype << 9 | // [9, 13] + policy.bits.wfh << 14 | // [14] + //policy.bits.padding << 15 | // [15] + policy.bits.verStretch << 16 | // [16, 23] + policy.bits.horStretch << 24); // [24, 31] + return stream << data; +} + +#define VALUE_OF_BITS(data, bitstart, bitcount) ((data >> bitstart) & ((1 << bitcount) -1)) + +/*! + \relates QLayoutPolicy + + Reads the size \a policy from the data stream \a stream. + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ +QDataStream &operator>>(QDataStream &stream, QLayoutPolicy &policy) +{ + quint32 data; + stream >> data; + policy.bits.horPolicy = VALUE_OF_BITS(data, 0, 4); + policy.bits.verPolicy = VALUE_OF_BITS(data, 4, 4); + policy.bits.hfw = VALUE_OF_BITS(data, 8, 1); + policy.bits.ctype = VALUE_OF_BITS(data, 9, 5); + policy.bits.wfh = VALUE_OF_BITS(data, 14, 1); + policy.bits.padding = 0; + policy.bits.verStretch = VALUE_OF_BITS(data, 16, 8); + policy.bits.horStretch = VALUE_OF_BITS(data, 24, 8); + return stream; +} +#endif // QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLayoutPolicy &p) +{ + dbg.nospace() << "QLayoutPolicy(horizontalPolicy = " << p.horizontalPolicy() + << ", verticalPolicy = " << p.verticalPolicy() << ')'; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/util/qlayoutpolicy_p.h b/src/gui/util/qlayoutpolicy_p.h new file mode 100644 index 0000000000..664afef1a4 --- /dev/null +++ b/src/gui/util/qlayoutpolicy_p.h @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLAYOUTPOLICY_H +#define QLAYOUTPOLICY_H + +#include <QtCore/qobject.h> +#include <QtCore/qnamespace.h> + +#ifndef QT_NO_DATASTREAM +# include <QtCore/qdatastream.h> +#endif + +QT_BEGIN_NAMESPACE + + +class QVariant; + +class Q_GUI_EXPORT QLayoutPolicy +{ + Q_ENUMS(Policy) + +public: + enum PolicyFlag { + GrowFlag = 1, + ExpandFlag = 2, + ShrinkFlag = 4, + IgnoreFlag = 8 + }; + + enum Policy { + Fixed = 0, + Minimum = GrowFlag, + Maximum = ShrinkFlag, + Preferred = GrowFlag | ShrinkFlag, + MinimumExpanding = GrowFlag | ExpandFlag, + Expanding = GrowFlag | ShrinkFlag | ExpandFlag, + Ignored = ShrinkFlag | GrowFlag | IgnoreFlag + }; + + enum ControlType { + DefaultType = 0x00000001, + ButtonBox = 0x00000002, + CheckBox = 0x00000004, + ComboBox = 0x00000008, + Frame = 0x00000010, + GroupBox = 0x00000020, + Label = 0x00000040, + Line = 0x00000080, + LineEdit = 0x00000100, + PushButton = 0x00000200, + RadioButton = 0x00000400, + Slider = 0x00000800, + SpinBox = 0x00001000, + TabWidget = 0x00002000, + ToolButton = 0x00004000 + }; + Q_DECLARE_FLAGS(ControlTypes, ControlType) + + QLayoutPolicy() : data(0) { } + + QLayoutPolicy(Policy horizontal, Policy vertical, ControlType type = DefaultType) + : data(0) { + bits.horPolicy = horizontal; + bits.verPolicy = vertical; + setControlType(type); + } + Policy horizontalPolicy() const { return static_cast<Policy>(bits.horPolicy); } + Policy verticalPolicy() const { return static_cast<Policy>(bits.verPolicy); } + ControlType controlType() const; + + void setHorizontalPolicy(Policy d) { bits.horPolicy = d; } + void setVerticalPolicy(Policy d) { bits.verPolicy = d; } + void setControlType(ControlType type); + + Qt::Orientations expandingDirections() const { + Qt::Orientations result; + if (verticalPolicy() & ExpandFlag) + result |= Qt::Vertical; + if (horizontalPolicy() & ExpandFlag) + result |= Qt::Horizontal; + return result; + } + + void setHeightForWidth(bool b) { bits.hfw = b; } + bool hasHeightForWidth() const { return bits.hfw; } + void setWidthForHeight(bool b) { bits.wfh = b; } + bool hasWidthForHeight() const { return bits.wfh; } + + bool operator==(const QLayoutPolicy& s) const { return data == s.data; } + bool operator!=(const QLayoutPolicy& s) const { return data != s.data; } + + int horizontalStretch() const { return static_cast<int>(bits.horStretch); } + int verticalStretch() const { return static_cast<int>(bits.verStretch); } + void setHorizontalStretch(int stretchFactor) { bits.horStretch = static_cast<quint32>(qBound(0, stretchFactor, 255)); } + void setVerticalStretch(int stretchFactor) { bits.verStretch = static_cast<quint32>(qBound(0, stretchFactor, 255)); } + + void transpose(); + + +private: +#ifndef QT_NO_DATASTREAM + friend QDataStream &operator<<(QDataStream &, const QLayoutPolicy &); + friend QDataStream &operator>>(QDataStream &, QLayoutPolicy &); +#endif + QLayoutPolicy(int i) : data(i) { } + + union { + struct { + quint32 horStretch : 8; + quint32 verStretch : 8; + quint32 horPolicy : 4; + quint32 verPolicy : 4; + quint32 ctype : 5; + quint32 hfw : 1; + quint32 wfh : 1; + quint32 padding : 1; // feel free to use + } bits; + quint32 data; + }; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QLayoutPolicy::ControlTypes) + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &, const QLayoutPolicy &); +QDataStream &operator>>(QDataStream &, QLayoutPolicy &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLayoutPolicy &); +#endif + +inline void QLayoutPolicy::transpose() { + Policy hData = horizontalPolicy(); + Policy vData = verticalPolicy(); + int hStretch = horizontalStretch(); + int vStretch = verticalStretch(); + setHorizontalPolicy(vData); + setVerticalPolicy(hData); + setHorizontalStretch(vStretch); + setVerticalStretch(hStretch); +} + +QT_END_NAMESPACE + +#endif // QLAYOUTPOLICY_H diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri index dfb221667e..79c83599b9 100644 --- a/src/gui/util/util.pri +++ b/src/gui/util/util.pri @@ -3,8 +3,14 @@ HEADERS += \ util/qdesktopservices.h \ util/qhexstring_p.h \ - util/qvalidator.h + util/qvalidator.h \ + util/qgridlayoutengine_p.h \ + util/qabstractlayoutstyleinfo_p.h \ + util/qlayoutpolicy_p.h SOURCES += \ util/qdesktopservices.cpp \ - util/qvalidator.cpp + util/qvalidator.cpp \ + util/qgridlayoutengine.cpp \ + util/qabstractlayoutstyleinfo.cpp \ + util/qlayoutpolicy.cpp |