src/gui/image/qpixmap_x11.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
--- a/src/gui/image/qpixmap_x11.cpp	Tue Jul 06 15:10:48 2010 +0300
+++ b/src/gui/image/qpixmap_x11.cpp	Wed Aug 18 10:37:55 2010 +0300
@@ -1250,10 +1250,8 @@
     pengine = 0;
 
     if (!X11) {
-#ifndef QT_NO_DEBUG
-        qWarning("~QX11PixmapData(): QPixmap objects must be destroyed before the QApplication"
-                 " object, otherwise the native pixmap object will be leaked.");
-#endif
+        // At this point, the X server will already have freed our resources,
+        // so there is nothing to do.
         return;
     }
 
@@ -1460,6 +1458,105 @@
     }
 }
 
+struct QXImageWrapper
+{
+    XImage *xi;
+};
+
+bool QX11PixmapData::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+    XImage *xi = xiWrapper.xi;
+
+    // ARGB32_Premultiplied
+    if (picture && depth() == 32)
+        return true;
+
+    Visual *visual = (Visual *)xinfo.visual();
+
+    // RGB32
+    if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000
+        && visual->green_mask == 0xff00 && visual->blue_mask == 0xff)
+        return true;
+
+    // RGB16
+    if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800
+        && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f)
+        return true;
+
+    return false;
+}
+
+QImage QX11PixmapData::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const
+{
+    XImage *xi = xiWrapper.xi;
+
+    QImage::Format format = QImage::Format_ARGB32_Premultiplied;
+    if (depth() == 24)
+        format = QImage::Format_RGB32;
+    else if (depth() == 16)
+        format = QImage::Format_RGB16;
+
+    QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format);
+    // take ownership
+    image.data_ptr()->own_data = true;
+    xi->data = 0;
+
+    // we may have to swap the byte order
+    if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
+        || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
+    {
+        for (int i=0; i < image.height(); i++) {
+            if (depth() == 16) {
+                ushort *p = (ushort*)image.scanLine(i);
+                ushort *end = p + image.width();
+                while (p < end) {
+                    *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
+                    p++;
+                }
+            } else {
+                uint *p = (uint*)image.scanLine(i);
+                uint *end = p + image.width();
+                while (p < end) {
+                    *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
+                         | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
+                    p++;
+                }
+            }
+        }
+    }
+
+    // fix-up alpha channel
+    if (format == QImage::Format_RGB32) {
+        QRgb *p = (QRgb *)image.bits();
+        for (int y = 0; y < xi->height; ++y) {
+            for (int x = 0; x < xi->width; ++x)
+                p[x] |= 0xff000000;
+            p += xi->bytes_per_line / 4;
+        }
+    }
+
+    XDestroyImage(xi);
+    return image;
+}
+
+QImage QX11PixmapData::toImage(const QRect &rect) const
+{
+    QXImageWrapper xiWrapper;
+    xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(),
+                             AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap);
+
+    Q_CHECK_PTR(xiWrapper.xi);
+    if (!xiWrapper.xi)
+        return QImage();
+
+    if (canTakeQImageFromXImage(xiWrapper))
+        return takeQImageFromXImage(xiWrapper);
+
+    QImage image = toImage(xiWrapper, rect);
+    qSafeXDestroyImage(xiWrapper.xi);
+    return image;
+}
+
 /*!
     Converts the pixmap to a QImage. Returns a null image if the
     conversion fails.
@@ -1477,6 +1574,13 @@
 
 QImage QX11PixmapData::toImage() const
 {
+    return toImage(QRect(0, 0, w, h));
+}
+
+QImage QX11PixmapData::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const
+{
+    XImage *xi = xiWrapper.xi;
+
     int d = depth();
     Visual *visual = (Visual *)xinfo.visual();
     bool trucol = (visual->c_class >= TrueColor) && d > 1;
@@ -1494,59 +1598,21 @@
         format = QImage::Format_RGB32;
     }
 
-    XImage *xi = XGetImage(X11->display, hd, 0, 0, w, h, AllPlanes,
-                           (d == 1) ? XYPixmap : ZPixmap);
-
-    Q_CHECK_PTR(xi);
-    if (!xi)
-        return QImage();
-
-    if (picture && depth() == 32) {
-        QImage image(w, h, QImage::Format_ARGB32_Premultiplied);
-        memcpy(image.bits(), xi->data, xi->bytes_per_line * xi->height);
-
-        // we may have to swap the byte order
-        if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
-            || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst))
-        {
-            for (int i=0; i < image.height(); i++) {
-                uint *p = (uint*)image.scanLine(i);
-                uint *end = p + image.width();
-                if ((xi->byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian)
-                    || (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian)) {
-                    while (p < end) {
-                        *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
-                             | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
-                        p++;
-                    }
-                } else if (xi->byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) {
-                    while (p < end) {
-                        *p = ((*p << 16) & 0x00ff0000) | ((*p >> 16) & 0x000000ff)
-                             | ((*p ) & 0xff00ff00);
-                        p++;
-                    }
-                }
-            }
-        }
-
-        // throw away image data
-        qSafeXDestroyImage(xi);
-
-        return image;
-    }
-
     if (d == 1 && xi->bitmap_bit_order == LSBFirst)
         format = QImage::Format_MonoLSB;
     if (x11_mask && format == QImage::Format_RGB32)
         format = QImage::Format_ARGB32;
 
-    QImage image(w, h, format);
+    QImage image(xi->width, xi->height, format);
     if (image.isNull())                        // could not create image
         return image;
 
     QImage alpha;
     if (x11_mask) {
-        alpha = mask().toImage();
+        if (rect.contains(QRect(0, 0, w, h)))
+            alpha = mask().toImage();
+        else
+            alpha = mask().toImage().copy(rect);
     }
     bool ale = alpha.format() == QImage::Format_MonoLSB;
 
@@ -1589,11 +1655,11 @@
         if (bppc > 8 && xi->byte_order == LSBFirst)
             bppc++;
 
-        for (int y = 0; y < h; ++y) {
+        for (int y = 0; y < xi->height; ++y) {
             uchar* asrc = x11_mask ? alpha.scanLine(y) : 0;
             dst = (QRgb *)image.scanLine(y);
             src = (uchar *)xi->data + xi->bytes_per_line*y;
-            for (int x = 0; x < w; x++) {
+            for (int x = 0; x < xi->width; x++) {
                 switch (bppc) {
                 case 8:
                     pixel = *src++;
@@ -1623,8 +1689,8 @@
                     src += 4;
                     break;
                 default:                        // should not really happen
-                    x = w;                        // leave loop
-                    y = h;
+                    x = xi->width;                        // leave loop
+                    y = xi->height;
                     pixel = 0;                // eliminate compiler warning
                     qWarning("QPixmap::convertToImage: Invalid depth %d", bppc);
                 }
@@ -1662,7 +1728,7 @@
     } else if (xi->bits_per_pixel == d) {        // compatible depth
         char *xidata = xi->data;                // copy each scanline
         int bpl = qMin(image.bytesPerLine(),xi->bytes_per_line);
-        for (int y=0; y<h; y++) {
+        for (int y=0; y<xi->height; y++) {
             memcpy(image.scanLine(y), xidata, bpl);
             xidata += xi->bytes_per_line;
         }
@@ -1688,17 +1754,17 @@
         bpl = image.bytesPerLine();
 
         if (x11_mask) {                         // which pixels are used?
-            for (int i = 0; i < h; i++) {
+            for (int i = 0; i < xi->height; i++) {
                 uchar* asrc = alpha.scanLine(i);
                 p = image.scanLine(i);
                 if (ale) {
-                    for (int x = 0; x < w; x++) {
+                    for (int x = 0; x < xi->width; x++) {
                         if (asrc[x >> 3] & (1 << (x & 7)))
                             use[*p] = 1;
                         ++p;
                     }
                 } else {
-                    for (int x = 0; x < w; x++) {
+                    for (int x = 0; x < xi->width; x++) {
                         if (asrc[x >> 3] & (0x80 >> (x & 7)))
                             use[*p] = 1;
                         ++p;
@@ -1706,7 +1772,7 @@
                 }
             }
         } else {
-            for (int i = 0; i < h; i++) {
+            for (int i = 0; i < xi->height; i++) {
                 p = image.scanLine(i);
                 end = p + bpl;
                 while (p < end)
@@ -1718,7 +1784,7 @@
             if (use[i])
                 pix[i] = ncols++;
         }
-        for (int i = 0; i < h; i++) {                        // translate pixels
+        for (int i = 0; i < xi->height; i++) {                        // translate pixels
             p = image.scanLine(i);
             end = p + bpl;
             while (p < end) {
@@ -1738,17 +1804,17 @@
                 // use first pixel in image (as good as any).
                 trans = image.scanLine(0)[0];
             }
-            for (int i = 0; i < h; i++) {
+            for (int i = 0; i < xi->height; i++) {
                 uchar* asrc = alpha.scanLine(i);
                 p = image.scanLine(i);
                 if (ale) {
-                    for (int x = 0; x < w; x++) {
+                    for (int x = 0; x < xi->width; x++) {
                         if (!(asrc[x >> 3] & (1 << (x & 7))))
                             *p = trans;
                         ++p;
                     }
                 } else {
-                    for (int x = 0; x < w; x++) {
+                    for (int x = 0; x < xi->width; x++) {
                         if (!(asrc[x >> 3] & (1 << (7 -(x & 7)))))
                             *p = trans;
                         ++p;
@@ -1766,8 +1832,6 @@
         }
     }
 
-    qSafeXDestroyImage(xi);
-
     return image;
 }