src/gui/image/qimage.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
--- a/src/gui/image/qimage.cpp	Tue Jul 06 15:10:48 2010 +0300
+++ b/src/gui/image/qimage.cpp	Wed Aug 18 10:37:55 2010 +0300
@@ -58,6 +58,7 @@
 #include <private/qmemrotate_p.h>
 #include <private/qpixmapdata_p.h>
 #include <private/qimagescale_p.h>
+#include <private/qsimd_p.h>
 
 #include <qhash.h>
 
@@ -209,7 +210,7 @@
         break;
     }
 
-    const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 8)
+    const int bytes_per_line = ((width * depth + 31) >> 5) << 2; // bytes per scanline (must be multiple of 4)
 
     // sanity check for potential overflows
     if (INT_MAX/depth < width
@@ -272,6 +273,8 @@
 
     switch (format) {
 
+    case QImage::Format_Mono:
+    case QImage::Format_MonoLSB:
     case QImage::Format_Indexed8:
         has_alpha_pixels = has_alpha_clut;
         break;
@@ -2272,6 +2275,8 @@
 
 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);
@@ -2296,6 +2301,209 @@
     }
 }
 
+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 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] = qt_colorConvert<quint16, quint32>(qRgb(i, i, i), 0);
+    } else {
+        // 1) convert the existing colors to RGB16
+        const int tableSize = data->colortable.size();
+        for (int i = 0; i < tableSize; ++i)
+            colorTableRGB16[i] = qt_colorConvert<quint16, quint32>(data->colortable.at(i), 0);
+        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) {
+        qt_memconvert(dst_data, src_data, data->width);
+        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);
@@ -3445,6 +3653,116 @@
     } // Format_ARGB4444_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
+    }, // Format_Mono
+    {
+       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,
+    }, // Format_Indexed8
+    {
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        convert_RGB_to_RGB16_inplace,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+    }, // Format_ARGB32
+    {
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        convert_ARGB_to_ARGB_PM_inplace,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+    }, // Format_ARGB32
+    {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    },  // Format_ARGB32_Premultiplied
+    {
+        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
+    }, // Format_ARGB8565_Premultiplied
+    {
+        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
+    }, // Format_ARGB6666_Premultiplied
+    {
+        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
+    }, // Format_ARGB8555_Premultiplied
+    {
+        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
+    }, // Format_RGB444
+    {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    } // Format_ARGB4444_Premultiplied
+};
+
+void qInitImageConversions()
+{
+    const uint features = qDetectCPUFeatures();
+    Q_UNUSED(features);
+
+#ifdef QT_HAVE_SSE2
+    if (features & 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;
+    }
+#endif
+}
+
 /*!
     Returns a copy of the image in the given \a format.
 
@@ -3797,7 +4115,7 @@
         }
         break;
     case Format_Indexed8:
-        if (index_or_rgb > (uint)d->colortable.size()) {
+        if (index_or_rgb >= (uint)d->colortable.size()) {
             qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
             return;
         }
@@ -4208,6 +4526,7 @@
     int w = width();
     int h = height();
     QImage m(w, h, Format_MonoLSB);
+    QIMAGE_SANITYCHECK_MEMORY(m);
     m.setColorCount(2);
     m.setColor(0, QColor(Qt::color0).rgba());
     m.setColor(1, QColor(Qt::color1).rgba());
@@ -4300,6 +4619,7 @@
     if (!d)
         return QImage();
     QImage maskImage(size(), QImage::Format_MonoLSB);
+    QIMAGE_SANITYCHECK_MEMORY(maskImage);
     maskImage.fill(0);
     uchar *s = maskImage.bits();
 
@@ -4360,6 +4680,7 @@
     int h = d->height;
     // Create result image, copy colormap
     QImage result(d->width, d->height, d->format);
+    QIMAGE_SANITYCHECK_MEMORY(result);
 
     // check if we ran out of of memory..
     if (!result.d)
@@ -4497,6 +4818,7 @@
     case Format_ARGB32:
     case Format_ARGB32_Premultiplied:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             uint *q = (uint*)res.scanLine(i);
             uint *p = (uint*)scanLine(i);
@@ -4510,6 +4832,7 @@
         break;
     case Format_RGB16:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             ushort *q = (ushort*)res.scanLine(i);
             const ushort *p = (const ushort*)scanLine(i);
@@ -4523,6 +4846,7 @@
         break;
     case Format_ARGB8565_Premultiplied:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             quint8 *p = (quint8*)scanLine(i);
             const quint8 *end = p + d->width * sizeof(qargb8565);
@@ -4535,6 +4859,7 @@
         break;
     case Format_RGB666:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             qrgb666 *q = reinterpret_cast<qrgb666*>(res.scanLine(i));
             const qrgb666 *p = reinterpret_cast<const qrgb666*>(scanLine(i));
@@ -4547,6 +4872,7 @@
         break;
     case Format_ARGB6666_Premultiplied:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             qargb6666 *q = reinterpret_cast<qargb6666*>(res.scanLine(i));
             const qargb6666 *p = reinterpret_cast<const qargb6666*>(scanLine(i));
@@ -4559,6 +4885,7 @@
         break;
     case Format_RGB555:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             ushort *q = (ushort*)res.scanLine(i);
             const ushort *p = (const ushort*)scanLine(i);
@@ -4572,6 +4899,7 @@
         break;
     case Format_ARGB8555_Premultiplied:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             quint8 *p = (quint8*)scanLine(i);
             const quint8 *end = p + d->width * sizeof(qargb8555);
@@ -4584,6 +4912,7 @@
         break;
     case Format_RGB888:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
             const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
@@ -4599,6 +4928,7 @@
         break;
     case Format_RGB444:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
             const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
@@ -4613,6 +4943,7 @@
         break;
     case Format_ARGB4444_Premultiplied:
         res = QImage(d->width, d->height, d->format);
+        QIMAGE_SANITYCHECK_MEMORY(res);
         for (int i = 0; i < d->height; i++) {
             quint8 *q = reinterpret_cast<quint8*>(res.scanLine(i));
             const quint8 *p = reinterpret_cast<const quint8*>(scanLine(i));
@@ -4812,7 +5143,7 @@
     or as a BMP image if the stream's version is 1. Note that writing
     the stream to a file will not produce a valid image file.
 
-    \sa QImage::save(), {Format of the QDataStream Operators}
+    \sa QImage::save(), {Serializing Qt Data Types}
 */
 
 QDataStream &operator<<(QDataStream &s, const QImage &image)
@@ -4838,7 +5169,7 @@
     Reads an image from the given \a stream and stores it in the given
     \a image.
 
-    \sa QImage::load(), {Format of the QDataStream Operators}
+    \sa QImage::load(), {Serializing Qt Data Types}
 */
 
 QDataStream &operator>>(QDataStream &s, QImage &image)
@@ -6261,6 +6592,18 @@
     return matrix * QTransform().translate(-delta.x(), -delta.y());
 }
 
+bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
+{
+    if (format == newFormat)
+        return true;
+
+    const InPlace_Image_Converter *const converterPtr = &inplace_converter_map[format][newFormat];
+    InPlace_Image_Converter converter = *converterPtr;
+    if (converter)
+        return converter(this, flags);
+    else
+        return false;
+}
 
 /*!
     \typedef QImage::DataPtr