| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QGuiApplication> |
| |
| #include <private/qdrawhelper_p.h> |
| #include <private/qimage_p.h> |
| #include <private/qimagepixmapcleanuphooks_p.h> |
| |
| #include "qxcbnativepainting.h" |
| #include "qpixmap_x11_p.h" |
| #include "qcolormap_x11_p.h" |
| #include "qpaintengine_x11_p.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| #if QT_POINTER_SIZE == 8 // 64-bit versions |
| |
| Q_ALWAYS_INLINE uint PREMUL(uint x) { |
| uint a = x >> 24; |
| quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a; |
| t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8; |
| t &= 0x000000ff00ff00ff; |
| return (uint(t)) | (uint(t >> 24)) | (a << 24); |
| } |
| |
| #else // 32-bit versions |
| |
| Q_ALWAYS_INLINE uint PREMUL(uint x) { |
| uint a = x >> 24; |
| uint t = (x & 0xff00ff) * a; |
| t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8; |
| t &= 0xff00ff; |
| |
| x = ((x >> 8) & 0xff) * a; |
| x = (x + ((x >> 8) & 0xff) + 0x80); |
| x &= 0xff00; |
| x |= t | (a << 24); |
| return x; |
| } |
| #endif |
| |
| |
| |
| struct QXImageWrapper |
| { |
| XImage *xi; |
| }; |
| |
| QPixmap qt_toX11Pixmap(const QImage &image) |
| { |
| QPlatformPixmap *data = |
| new QX11PlatformPixmap(image.depth() == 1 |
| ? QPlatformPixmap::BitmapType |
| : QPlatformPixmap::PixmapType); |
| |
| data->fromImage(image, Qt::AutoColor); |
| |
| return QPixmap(data); |
| } |
| |
| QPixmap qt_toX11Pixmap(const QPixmap &pixmap) |
| { |
| if (pixmap.isNull()) |
| return QPixmap(); |
| |
| if (QPixmap(pixmap).data_ptr()->classId() == QPlatformPixmap::X11Class) |
| return pixmap; |
| |
| return qt_toX11Pixmap(pixmap.toImage()); |
| } |
| |
| // For thread-safety: |
| // image->data does not belong to X11, so we must free it ourselves. |
| |
| inline static void qSafeXDestroyImage(XImage *x) |
| { |
| if (x->data) { |
| free(x->data); |
| x->data = 0; |
| } |
| XDestroyImage(x); |
| } |
| |
| QBitmap QX11PlatformPixmap::mask_to_bitmap(int screen) const |
| { |
| if (!x11_mask) |
| return QBitmap(); |
| qt_x11SetDefaultScreen(screen); |
| QBitmap bm(w, h); |
| QX11PlatformPixmap *that = qt_x11Pixmap(bm); |
| const QXcbX11Info *x = that->x11_info(); |
| GC gc = XCreateGC(x->display(), that->handle(), 0, 0); |
| XCopyArea(x->display(), x11_mask, that->handle(), gc, 0, 0, |
| that->width(), that->height(), 0, 0); |
| XFreeGC(x->display(), gc); |
| return bm; |
| } |
| |
| void QX11PlatformPixmap::bitmapFromImage(const QImage &image) |
| { |
| w = image.width(); |
| h = image.height(); |
| d = 1; |
| is_null = (w <= 0 || h <= 0); |
| hd = createBitmapFromImage(image); |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) |
| picture = XRenderCreatePicture(xinfo.display(), hd, |
| XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0); |
| #endif // QT_CONFIG(xrender) |
| } |
| |
| bool QX11PlatformPixmap::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const |
| { |
| XImage *xi = xiWrapper.xi; |
| |
| if (xi->format != ZPixmap) |
| return false; |
| |
| // ARGB32_Premultiplied |
| if (picture && depth() == 32) |
| return true; |
| |
| // RGB32 |
| if (depth() == 24 && xi->bits_per_pixel == 32 && xi->red_mask == 0xff0000 |
| && xi->green_mask == 0xff00 && xi->blue_mask == 0xff) |
| return true; |
| |
| // RGB16 |
| if (depth() == 16 && xi->bits_per_pixel == 16 && xi->red_mask == 0xf800 |
| && xi->green_mask == 0x7e0 && xi->blue_mask == 0x1f) |
| return true; |
| |
| return false; |
| } |
| |
| QImage QX11PlatformPixmap::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); |
| image.setDevicePixelRatio(devicePixelRatio()); |
| // 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; |
| } |
| |
| XID QX11PlatformPixmap::bitmap_to_mask(const QBitmap &bitmap, int screen) |
| { |
| if (bitmap.isNull()) |
| return 0; |
| QBitmap bm = bitmap; |
| qt_x11SetScreen(bm, screen); |
| |
| QX11PlatformPixmap *that = qt_x11Pixmap(bm); |
| const QXcbX11Info *x = that->x11_info(); |
| Pixmap mask = XCreatePixmap(x->display(), RootWindow(x->display(), screen), |
| that->width(), that->height(), 1); |
| GC gc = XCreateGC(x->display(), mask, 0, 0); |
| XCopyArea(x->display(), that->handle(), mask, gc, 0, 0, |
| that->width(), that->height(), 0, 0); |
| XFreeGC(x->display(), gc); |
| return mask; |
| } |
| |
| Drawable qt_x11Handle(const QPixmap &pixmap) |
| { |
| if (pixmap.isNull()) |
| return XNone; |
| |
| if (pixmap.handle()->classId() != QPlatformPixmap::X11Class) |
| return XNone; |
| |
| return static_cast<const QX11PlatformPixmap *>(pixmap.handle())->handle(); |
| } |
| |
| |
| /***************************************************************************** |
| Internal functions |
| *****************************************************************************/ |
| |
| //extern const uchar *qt_get_bitflip_array(); // defined in qimage.cpp |
| |
| // Returns position of highest bit set or -1 if none |
| static int highest_bit(uint v) |
| { |
| int i; |
| uint b = (uint)1 << 31; |
| for (i=31; ((b & v) == 0) && i>=0; i--) |
| b >>= 1; |
| return i; |
| } |
| |
| // Counts the number of bits set in 'v' |
| static uint n_bits(uint v) |
| { |
| int i = 0; |
| while (v) { |
| v = v & (v - 1); |
| i++; |
| } |
| return i; |
| } |
| |
| static uint *red_scale_table = 0; |
| static uint *green_scale_table = 0; |
| static uint *blue_scale_table = 0; |
| |
| static void cleanup_scale_tables() |
| { |
| delete[] red_scale_table; |
| delete[] green_scale_table; |
| delete[] blue_scale_table; |
| } |
| |
| /* |
| Could do smart bitshifting, but the "obvious" algorithm only works for |
| nBits >= 4. This is more robust. |
| */ |
| static void build_scale_table(uint **table, uint nBits) |
| { |
| if (nBits > 7) { |
| qWarning("build_scale_table: internal error, nBits = %i", nBits); |
| return; |
| } |
| if (!*table) { |
| static bool firstTable = true; |
| if (firstTable) { |
| qAddPostRoutine(cleanup_scale_tables); |
| firstTable = false; |
| } |
| *table = new uint[256]; |
| } |
| int maxVal = (1 << nBits) - 1; |
| int valShift = 8 - nBits; |
| int i; |
| for (i = 0 ; i < maxVal + 1 ; i++) |
| (*table)[i << valShift] = i*255/maxVal; |
| } |
| |
| static int defaultScreen = -1; |
| |
| int qt_x11SetDefaultScreen(int screen) |
| { |
| int old = defaultScreen; |
| defaultScreen = screen; |
| return old; |
| } |
| |
| void qt_x11SetScreen(QPixmap &pixmap, int screen) |
| { |
| if (pixmap.paintingActive()) { |
| qWarning("qt_x11SetScreen(): Cannot change screens during painting"); |
| return; |
| } |
| |
| if (pixmap.isNull()) |
| return; |
| |
| if (pixmap.handle()->classId() != QPlatformPixmap::X11Class) |
| return; |
| |
| if (screen < 0) |
| screen = QXcbX11Info::appScreen(); |
| |
| QX11PlatformPixmap *pm = static_cast<QX11PlatformPixmap *>(pixmap.handle()); |
| if (screen == pm->xinfo.screen()) |
| return; // nothing to do |
| |
| if (pixmap.isNull()) { |
| pm->xinfo = QXcbX11Info::fromScreen(screen); |
| return; |
| } |
| |
| #if 0 |
| qDebug("qt_x11SetScreen for %p from %d to %d. Size is %d/%d", pm, pm->xinfo.screen(), screen, pm->width(), pm->height()); |
| #endif |
| |
| qt_x11SetDefaultScreen(screen); |
| pixmap = qt_toX11Pixmap(pixmap.toImage()); |
| } |
| |
| /***************************************************************************** |
| QPixmap member functions |
| *****************************************************************************/ |
| |
| QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); |
| int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; |
| |
| QX11PlatformPixmap::QX11PlatformPixmap(PixelType pixelType) |
| : QPlatformPixmap(pixelType, X11Class), hd(0), |
| flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0), |
| dpr(1.0), pengine(0) |
| {} |
| |
| QX11PlatformPixmap::~QX11PlatformPixmap() |
| { |
| // Cleanup hooks have to be called before the handles are freed |
| if (is_cached) { |
| QImagePixmapCleanupHooks::executePlatformPixmapDestructionHooks(this); |
| is_cached = false; |
| } |
| |
| release(); |
| } |
| |
| QPlatformPixmap *QX11PlatformPixmap::createCompatiblePlatformPixmap() const |
| { |
| QX11PlatformPixmap *p = new QX11PlatformPixmap(pixelType()); |
| p->setDevicePixelRatio(devicePixelRatio()); |
| return p; |
| } |
| |
| void QX11PlatformPixmap::resize(int width, int height) |
| { |
| setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); |
| |
| w = width; |
| h = height; |
| is_null = (w <= 0 || h <= 0); |
| |
| if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) { |
| xinfo = QXcbX11Info::fromScreen(defaultScreen); |
| } |
| |
| int dd = xinfo.depth(); |
| |
| if (qt_x11_preferred_pixmap_depth) |
| dd = qt_x11_preferred_pixmap_depth; |
| |
| bool make_null = w <= 0 || h <= 0; // create null pixmap |
| d = (pixelType() == BitmapType ? 1 : dd); |
| if (make_null || d == 0) { |
| w = 0; |
| h = 0; |
| is_null = true; |
| hd = 0; |
| picture = 0; |
| d = 0; |
| if (!make_null) |
| qWarning("QPixmap: Invalid pixmap parameters"); |
| return; |
| } |
| hd = XCreatePixmap(xinfo.display(), |
| RootWindow(xinfo.display(), xinfo.screen()), |
| w, h, d); |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| XRenderPictFormat *format = d == 1 |
| ? XRenderFindStandardFormat(xinfo.display(), PictStandardA1) |
| : XRenderFindVisualFormat(xinfo.display(), (Visual *) xinfo.visual()); |
| picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0); |
| } |
| #endif // QT_CONFIG(xrender) |
| } |
| |
| struct QX11AlphaDetector |
| { |
| bool hasAlpha() const { |
| if (checked) |
| return has; |
| // Will implicitly also check format and return quickly for opaque types... |
| checked = true; |
| has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels(); |
| return has; |
| } |
| |
| bool hasXRenderAndAlpha() const { |
| if (!X11->use_xrender) |
| return false; |
| return hasAlpha(); |
| } |
| |
| QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags) |
| : image(i), checked(false), has(false) |
| { |
| if (flags & Qt::NoOpaqueDetection) { |
| checked = true; |
| has = image->hasAlphaChannel(); |
| } |
| } |
| |
| const QImage *image; |
| mutable bool checked; |
| mutable bool has; |
| }; |
| |
| void QX11PlatformPixmap::fromImage(const QImage &img, Qt::ImageConversionFlags flags) |
| { |
| setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); |
| |
| w = img.width(); |
| h = img.height(); |
| d = img.depth(); |
| is_null = (w <= 0 || h <= 0); |
| setDevicePixelRatio(img.devicePixelRatio()); |
| |
| if (is_null) { |
| w = h = 0; |
| return; |
| } |
| |
| if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) { |
| xinfo = QXcbX11Info::fromScreen(defaultScreen); |
| } |
| |
| if (pixelType() == BitmapType) { |
| bitmapFromImage(img); |
| return; |
| } |
| |
| if (uint(w) >= 32768 || uint(h) >= 32768) { |
| w = h = 0; |
| is_null = true; |
| return; |
| } |
| |
| QX11AlphaDetector alphaCheck(&img, flags); |
| int dd = alphaCheck.hasXRenderAndAlpha() ? 32 : xinfo.depth(); |
| |
| if (qt_x11_preferred_pixmap_depth) |
| dd = qt_x11_preferred_pixmap_depth; |
| |
| QImage image = img; |
| |
| // must be monochrome |
| if (dd == 1 || (flags & Qt::ColorMode_Mask) == Qt::MonoOnly) { |
| if (d != 1) { |
| // dither |
| image = image.convertToFormat(QImage::Format_MonoLSB, flags); |
| d = 1; |
| } |
| } else { // can be both |
| bool conv8 = false; |
| if (d > 8 && dd <= 8) { // convert to 8 bit |
| if ((flags & Qt::DitherMode_Mask) == Qt::AutoDither) |
| flags = (flags & ~Qt::DitherMode_Mask) |
| | Qt::PreferDither; |
| conv8 = true; |
| } else if ((flags & Qt::ColorMode_Mask) == Qt::ColorOnly) { |
| conv8 = (d == 1); // native depth wanted |
| } else if (d == 1) { |
| if (image.colorCount() == 2) { |
| QRgb c0 = image.color(0); // Auto: convert to best |
| QRgb c1 = image.color(1); |
| conv8 = qMin(c0,c1) != qRgb(0,0,0) || qMax(c0,c1) != qRgb(255,255,255); |
| } else { |
| // eg. 1-color monochrome images (they do exist). |
| conv8 = true; |
| } |
| } |
| if (conv8) { |
| image = image.convertToFormat(QImage::Format_Indexed8, flags); |
| d = 8; |
| } |
| } |
| |
| if (d == 1 || image.format() > QImage::Format_ARGB32_Premultiplied) { |
| QImage::Format fmt = QImage::Format_RGB32; |
| if (alphaCheck.hasXRenderAndAlpha() && d > 1) |
| fmt = QImage::Format_ARGB32_Premultiplied; |
| image = image.convertToFormat(fmt, flags); |
| fromImage(image, Qt::AutoColor); |
| return; |
| } |
| |
| Display *dpy = xinfo.display(); |
| Visual *visual = (Visual *)xinfo.visual(); |
| XImage *xi = 0; |
| bool trucol = (visual->c_class >= TrueColor); |
| size_t nbytes = image.sizeInBytes(); |
| uchar *newbits= 0; |
| |
| #if QT_CONFIG(xrender) |
| if (alphaCheck.hasXRenderAndAlpha()) { |
| const QImage &cimage = image; |
| |
| d = 32; |
| |
| if (QXcbX11Info::appDepth() != d) { |
| xinfo.setDepth(d); |
| } |
| |
| hd = XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()), w, h, d); |
| picture = XRenderCreatePicture(dpy, hd, |
| XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0); |
| |
| xi = XCreateImage(dpy, visual, d, ZPixmap, 0, 0, w, h, 32, 0); |
| Q_CHECK_PTR(xi); |
| newbits = (uchar *)malloc(xi->bytes_per_line*h); |
| Q_CHECK_PTR(newbits); |
| xi->data = (char *)newbits; |
| |
| switch (cimage.format()) { |
| case QImage::Format_Indexed8: { |
| QVector<QRgb> colorTable = cimage.colorTable(); |
| uint *xidata = (uint *)xi->data; |
| for (int y = 0; y < h; ++y) { |
| const uchar *p = cimage.scanLine(y); |
| for (int x = 0; x < w; ++x) { |
| const QRgb rgb = colorTable[p[x]]; |
| const int a = qAlpha(rgb); |
| if (a == 0xff) |
| *xidata = rgb; |
| else |
| // RENDER expects premultiplied alpha |
| *xidata = qRgba(qt_div_255(qRed(rgb) * a), |
| qt_div_255(qGreen(rgb) * a), |
| qt_div_255(qBlue(rgb) * a), |
| a); |
| ++xidata; |
| } |
| } |
| } |
| break; |
| case QImage::Format_RGB32: { |
| uint *xidata = (uint *)xi->data; |
| for (int y = 0; y < h; ++y) { |
| const QRgb *p = (const QRgb *) cimage.scanLine(y); |
| for (int x = 0; x < w; ++x) |
| *xidata++ = p[x] | 0xff000000; |
| } |
| } |
| break; |
| case QImage::Format_ARGB32: { |
| uint *xidata = (uint *)xi->data; |
| for (int y = 0; y < h; ++y) { |
| const QRgb *p = (const QRgb *) cimage.scanLine(y); |
| for (int x = 0; x < w; ++x) { |
| const QRgb rgb = p[x]; |
| const int a = qAlpha(rgb); |
| if (a == 0xff) |
| *xidata = rgb; |
| else |
| // RENDER expects premultiplied alpha |
| *xidata = qRgba(qt_div_255(qRed(rgb) * a), |
| qt_div_255(qGreen(rgb) * a), |
| qt_div_255(qBlue(rgb) * a), |
| a); |
| ++xidata; |
| } |
| } |
| |
| } |
| break; |
| case QImage::Format_ARGB32_Premultiplied: { |
| uint *xidata = (uint *)xi->data; |
| for (int y = 0; y < h; ++y) { |
| const QRgb *p = (const QRgb *) cimage.scanLine(y); |
| memcpy(xidata, p, w*sizeof(QRgb)); |
| xidata += w; |
| } |
| } |
| break; |
| default: |
| Q_ASSERT(false); |
| } |
| |
| if ((xi->byte_order == MSBFirst) != (QSysInfo::ByteOrder == QSysInfo::BigEndian)) { |
| uint *xidata = (uint *)xi->data; |
| uint *xiend = xidata + w*h; |
| while (xidata < xiend) { |
| *xidata = (*xidata >> 24) |
| | ((*xidata >> 8) & 0xff00) |
| | ((*xidata << 8) & 0xff0000) |
| | (*xidata << 24); |
| ++xidata; |
| } |
| } |
| |
| GC gc = XCreateGC(dpy, hd, 0, 0); |
| XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h); |
| XFreeGC(dpy, gc); |
| |
| qSafeXDestroyImage(xi); |
| |
| return; |
| } |
| #endif // QT_CONFIG(xrender) |
| |
| if (trucol) { // truecolor display |
| if (image.format() == QImage::Format_ARGB32_Premultiplied) |
| image = image.convertToFormat(QImage::Format_ARGB32); |
| |
| const QImage &cimage = image; |
| QRgb pix[256]; // pixel translation table |
| const bool d8 = (d == 8); |
| const uint red_mask = (uint)visual->red_mask; |
| const uint green_mask = (uint)visual->green_mask; |
| const uint blue_mask = (uint)visual->blue_mask; |
| const int red_shift = highest_bit(red_mask) - 7; |
| const int green_shift = highest_bit(green_mask) - 7; |
| const int blue_shift = highest_bit(blue_mask) - 7; |
| const uint rbits = highest_bit(red_mask) - lowest_bit(red_mask) + 1; |
| const uint gbits = highest_bit(green_mask) - lowest_bit(green_mask) + 1; |
| const uint bbits = highest_bit(blue_mask) - lowest_bit(blue_mask) + 1; |
| |
| if (d8) { // setup pixel translation |
| QVector<QRgb> ctable = cimage.colorTable(); |
| for (int i=0; i < cimage.colorCount(); i++) { |
| int r = qRed (ctable[i]); |
| int g = qGreen(ctable[i]); |
| int b = qBlue (ctable[i]); |
| r = red_shift > 0 ? r << red_shift : r >> -red_shift; |
| g = green_shift > 0 ? g << green_shift : g >> -green_shift; |
| b = blue_shift > 0 ? b << blue_shift : b >> -blue_shift; |
| pix[i] = (b & blue_mask) | (g & green_mask) | (r & red_mask) |
| | ~(blue_mask | green_mask | red_mask); |
| } |
| } |
| |
| xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0); |
| Q_CHECK_PTR(xi); |
| newbits = (uchar *)malloc(xi->bytes_per_line*h); |
| Q_CHECK_PTR(newbits); |
| if (!newbits) // no memory |
| return; |
| int bppc = xi->bits_per_pixel; |
| |
| bool contig_bits = n_bits(red_mask) == rbits && |
| n_bits(green_mask) == gbits && |
| n_bits(blue_mask) == bbits; |
| bool dither_tc = |
| // Want it? |
| (flags & Qt::Dither_Mask) != Qt::ThresholdDither && |
| (flags & Qt::DitherMode_Mask) != Qt::AvoidDither && |
| // Need it? |
| bppc < 24 && !d8 && |
| // Can do it? (Contiguous bits?) |
| contig_bits; |
| |
| static bool init=false; |
| static int D[16][16]; |
| if (dither_tc && !init) { |
| // I also contributed this code to XV - WWA. |
| /* |
| The dither matrix, D, is obtained with this formula: |
| |
| D2 = [0 2] |
| [3 1] |
| |
| |
| D2*n = [4*Dn 4*Dn+2*Un] |
| [4*Dn+3*Un 4*Dn+1*Un] |
| */ |
| int n,i,j; |
| init=1; |
| |
| /* Set D2 */ |
| D[0][0]=0; |
| D[1][0]=2; |
| D[0][1]=3; |
| D[1][1]=1; |
| |
| /* Expand using recursive definition given above */ |
| for (n=2; n<16; n*=2) { |
| for (i=0; i<n; i++) { |
| for (j=0; j<n; j++) { |
| D[i][j]*=4; |
| D[i+n][j]=D[i][j]+2; |
| D[i][j+n]=D[i][j]+3; |
| D[i+n][j+n]=D[i][j]+1; |
| } |
| } |
| } |
| init=true; |
| } |
| |
| enum { BPP8, |
| BPP16_565, BPP16_555, |
| BPP16_MSB, BPP16_LSB, |
| BPP24_888, |
| BPP24_MSB, BPP24_LSB, |
| BPP32_8888, |
| BPP32_MSB, BPP32_LSB |
| } mode = BPP8; |
| |
| bool same_msb_lsb = (xi->byte_order == MSBFirst) == (QSysInfo::ByteOrder == QSysInfo::BigEndian); |
| |
| if (bppc == 8) // 8 bit |
| mode = BPP8; |
| else if (bppc == 16) { // 16 bit MSB/LSB |
| if (red_shift == 8 && green_shift == 3 && blue_shift == -3 && !d8 && same_msb_lsb) |
| mode = BPP16_565; |
| else if (red_shift == 7 && green_shift == 2 && blue_shift == -3 && !d8 && same_msb_lsb) |
| mode = BPP16_555; |
| else |
| mode = (xi->byte_order == LSBFirst) ? BPP16_LSB : BPP16_MSB; |
| } else if (bppc == 24) { // 24 bit MSB/LSB |
| if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb) |
| mode = BPP24_888; |
| else |
| mode = (xi->byte_order == LSBFirst) ? BPP24_LSB : BPP24_MSB; |
| } else if (bppc == 32) { // 32 bit MSB/LSB |
| if (red_shift == 16 && green_shift == 8 && blue_shift == 0 && !d8 && same_msb_lsb) |
| mode = BPP32_8888; |
| else |
| mode = (xi->byte_order == LSBFirst) ? BPP32_LSB : BPP32_MSB; |
| } else |
| qFatal("Logic error 3"); |
| |
| #define GET_PIXEL \ |
| uint pixel; \ |
| if (d8) pixel = pix[*src++]; \ |
| else { \ |
| int r = qRed (*p); \ |
| int g = qGreen(*p); \ |
| int b = qBlue (*p++); \ |
| r = red_shift > 0 \ |
| ? r << red_shift : r >> -red_shift; \ |
| g = green_shift > 0 \ |
| ? g << green_shift : g >> -green_shift; \ |
| b = blue_shift > 0 \ |
| ? b << blue_shift : b >> -blue_shift; \ |
| pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask) \ |
| | ~(blue_mask | green_mask | red_mask); \ |
| } |
| |
| #define GET_PIXEL_DITHER_TC \ |
| int r = qRed (*p); \ |
| int g = qGreen(*p); \ |
| int b = qBlue (*p++); \ |
| const int thres = D[x%16][y%16]; \ |
| if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \ |
| > thres) \ |
| r += (1<<(8-rbits)); \ |
| if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \ |
| > thres) \ |
| g += (1<<(8-gbits)); \ |
| if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \ |
| > thres) \ |
| b += (1<<(8-bbits)); \ |
| r = red_shift > 0 \ |
| ? r << red_shift : r >> -red_shift; \ |
| g = green_shift > 0 \ |
| ? g << green_shift : g >> -green_shift; \ |
| b = blue_shift > 0 \ |
| ? b << blue_shift : b >> -blue_shift; \ |
| uint pixel = (r & red_mask)|(g & green_mask) | (b & blue_mask); |
| |
| // again, optimized case |
| // can't be optimized that much :( |
| #define GET_PIXEL_DITHER_TC_OPT(red_shift,green_shift,blue_shift,red_mask,green_mask,blue_mask, \ |
| rbits,gbits,bbits) \ |
| const int thres = D[x%16][y%16]; \ |
| int r = qRed (*p); \ |
| if (r <= (255-(1<<(8-rbits))) && ((r<<rbits) & 255) \ |
| > thres) \ |
| r += (1<<(8-rbits)); \ |
| int g = qGreen(*p); \ |
| if (g <= (255-(1<<(8-gbits))) && ((g<<gbits) & 255) \ |
| > thres) \ |
| g += (1<<(8-gbits)); \ |
| int b = qBlue (*p++); \ |
| if (b <= (255-(1<<(8-bbits))) && ((b<<bbits) & 255) \ |
| > thres) \ |
| b += (1<<(8-bbits)); \ |
| uint pixel = ((r red_shift) & red_mask) \ |
| | ((g green_shift) & green_mask) \ |
| | ((b blue_shift) & blue_mask); |
| |
| #define CYCLE(body) \ |
| for (int y=0; y<h; y++) { \ |
| const uchar* src = cimage.scanLine(y); \ |
| uchar* dst = newbits + xi->bytes_per_line*y; \ |
| const QRgb* p = (const QRgb *)src; \ |
| body \ |
| } |
| |
| if (dither_tc) { |
| switch (mode) { |
| case BPP16_565: |
| CYCLE( |
| quint16* dst16 = (quint16*)dst; |
| for (int x=0; x<w; x++) { |
| GET_PIXEL_DITHER_TC_OPT(<<8,<<3,>>3,0xf800,0x7e0,0x1f,5,6,5) |
| *dst16++ = pixel; |
| } |
| ) |
| break; |
| case BPP16_555: |
| CYCLE( |
| quint16* dst16 = (quint16*)dst; |
| for (int x=0; x<w; x++) { |
| GET_PIXEL_DITHER_TC_OPT(<<7,<<2,>>3,0x7c00,0x3e0,0x1f,5,5,5) |
| *dst16++ = pixel; |
| } |
| ) |
| break; |
| case BPP16_MSB: // 16 bit MSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL_DITHER_TC |
| *dst++ = (pixel >> 8); |
| *dst++ = pixel; |
| } |
| ) |
| break; |
| case BPP16_LSB: // 16 bit LSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL_DITHER_TC |
| *dst++ = pixel; |
| *dst++ = pixel >> 8; |
| } |
| ) |
| break; |
| default: |
| qFatal("Logic error"); |
| } |
| } else { |
| switch (mode) { |
| case BPP8: // 8 bit |
| CYCLE( |
| Q_UNUSED(p); |
| for (int x=0; x<w; x++) |
| *dst++ = pix[*src++]; |
| ) |
| break; |
| case BPP16_565: |
| CYCLE( |
| quint16* dst16 = (quint16*)dst; |
| for (int x = 0; x < w; x++) { |
| *dst16++ = ((*p >> 8) & 0xf800) |
| | ((*p >> 5) & 0x7e0) |
| | ((*p >> 3) & 0x1f); |
| ++p; |
| } |
| ) |
| break; |
| case BPP16_555: |
| CYCLE( |
| quint16* dst16 = (quint16*)dst; |
| for (int x=0; x<w; x++) { |
| *dst16++ = ((*p >> 9) & 0x7c00) |
| | ((*p >> 6) & 0x3e0) |
| | ((*p >> 3) & 0x1f); |
| ++p; |
| } |
| ) |
| break; |
| case BPP16_MSB: // 16 bit MSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = (pixel >> 8); |
| *dst++ = pixel; |
| } |
| ) |
| break; |
| case BPP16_LSB: // 16 bit LSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = pixel; |
| *dst++ = pixel >> 8; |
| } |
| ) |
| break; |
| case BPP24_888: |
| CYCLE( |
| if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { |
| for (int x=0; x<w; x++) { |
| *dst++ = qRed (*p); |
| *dst++ = qGreen(*p); |
| *dst++ = qBlue (*p++); |
| } |
| } else { |
| for (int x=0; x<w; x++) { |
| *dst++ = qBlue (*p); |
| *dst++ = qGreen(*p); |
| *dst++ = qRed (*p++); |
| } |
| } |
| ) |
| break; |
| case BPP24_MSB: // 24 bit MSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = pixel >> 16; |
| *dst++ = pixel >> 8; |
| *dst++ = pixel; |
| } |
| ) |
| break; |
| case BPP24_LSB: // 24 bit LSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = pixel; |
| *dst++ = pixel >> 8; |
| *dst++ = pixel >> 16; |
| } |
| ) |
| break; |
| case BPP32_8888: |
| CYCLE( |
| memcpy(dst, p, w * 4); |
| ) |
| break; |
| case BPP32_MSB: // 32 bit MSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = pixel >> 24; |
| *dst++ = pixel >> 16; |
| *dst++ = pixel >> 8; |
| *dst++ = pixel; |
| } |
| ) |
| break; |
| case BPP32_LSB: // 32 bit LSB |
| CYCLE( |
| for (int x=0; x<w; x++) { |
| GET_PIXEL |
| *dst++ = pixel; |
| *dst++ = pixel >> 8; |
| *dst++ = pixel >> 16; |
| *dst++ = pixel >> 24; |
| } |
| ) |
| break; |
| default: |
| qFatal("Logic error 2"); |
| } |
| } |
| xi->data = (char *)newbits; |
| } |
| |
| if (d == 8 && !trucol) { // 8 bit pixmap |
| int pop[256]; // pixel popularity |
| |
| if (image.colorCount() == 0) |
| image.setColorCount(1); |
| |
| const QImage &cimage = image; |
| memset(pop, 0, sizeof(int)*256); // reset popularity array |
| for (int i = 0; i < h; i++) { // for each scanline... |
| const uchar* p = cimage.scanLine(i); |
| const uchar *end = p + w; |
| while (p < end) // compute popularity |
| pop[*p++]++; |
| } |
| |
| newbits = (uchar *)malloc(nbytes); // copy image into newbits |
| Q_CHECK_PTR(newbits); |
| if (!newbits) // no memory |
| return; |
| uchar* p = newbits; |
| memcpy(p, cimage.bits(), nbytes); // copy image data into newbits |
| |
| /* |
| * The code below picks the most important colors. It is based on the |
| * diversity algorithm, implemented in XV 3.10. XV is (C) by John Bradley. |
| */ |
| |
| struct PIX { // pixel sort element |
| uchar r,g,b,n; // color + pad |
| int use; // popularity |
| int index; // index in colormap |
| int mindist; |
| }; |
| int ncols = 0; |
| for (int i=0; i< cimage.colorCount(); i++) { // compute number of colors |
| if (pop[i] > 0) |
| ncols++; |
| } |
| for (int i = cimage.colorCount(); i < 256; i++) // ignore out-of-range pixels |
| pop[i] = 0; |
| |
| // works since we make sure above to have at least |
| // one color in the image |
| if (ncols == 0) |
| ncols = 1; |
| |
| PIX pixarr[256]; // pixel array |
| PIX pixarr_sorted[256]; // pixel array (sorted) |
| memset(pixarr, 0, ncols*sizeof(PIX)); |
| PIX *px = &pixarr[0]; |
| int maxpop = 0; |
| int maxpix = 0; |
| uint j = 0; |
| QVector<QRgb> ctable = cimage.colorTable(); |
| for (int i = 0; i < 256; i++) { // init pixel array |
| if (pop[i] > 0) { |
| px->r = qRed (ctable[i]); |
| px->g = qGreen(ctable[i]); |
| px->b = qBlue (ctable[i]); |
| px->n = 0; |
| px->use = pop[i]; |
| if (pop[i] > maxpop) { // select most popular entry |
| maxpop = pop[i]; |
| maxpix = j; |
| } |
| px->index = i; |
| px->mindist = 1000000; |
| px++; |
| j++; |
| } |
| } |
| pixarr_sorted[0] = pixarr[maxpix]; |
| pixarr[maxpix].use = 0; |
| |
| for (int i = 1; i < ncols; i++) { // sort pixels |
| int minpix = -1, mindist = -1; |
| px = &pixarr_sorted[i-1]; |
| int r = px->r; |
| int g = px->g; |
| int b = px->b; |
| int dist; |
| if ((i & 1) || i<10) { // sort on max distance |
| for (int j=0; j<ncols; j++) { |
| px = &pixarr[j]; |
| if (px->use) { |
| dist = (px->r - r)*(px->r - r) + |
| (px->g - g)*(px->g - g) + |
| (px->b - b)*(px->b - b); |
| if (px->mindist > dist) |
| px->mindist = dist; |
| if (px->mindist > mindist) { |
| mindist = px->mindist; |
| minpix = j; |
| } |
| } |
| } |
| } else { // sort on max popularity |
| for (int j=0; j<ncols; j++) { |
| px = &pixarr[j]; |
| if (px->use) { |
| dist = (px->r - r)*(px->r - r) + |
| (px->g - g)*(px->g - g) + |
| (px->b - b)*(px->b - b); |
| if (px->mindist > dist) |
| px->mindist = dist; |
| if (px->use > mindist) { |
| mindist = px->use; |
| minpix = j; |
| } |
| } |
| } |
| } |
| pixarr_sorted[i] = pixarr[minpix]; |
| pixarr[minpix].use = 0; |
| } |
| |
| QXcbColormap cmap = QXcbColormap::instance(xinfo.screen()); |
| uint pix[256]; // pixel translation table |
| px = &pixarr_sorted[0]; |
| for (int i = 0; i < ncols; i++) { // allocate colors |
| QColor c(px->r, px->g, px->b); |
| pix[px->index] = cmap.pixel(c); |
| px++; |
| } |
| |
| p = newbits; |
| for (size_t i = 0; i < nbytes; i++) { // translate pixels |
| *p = pix[*p]; |
| p++; |
| } |
| } |
| |
| if (!xi) { // X image not created |
| xi = XCreateImage(dpy, visual, dd, ZPixmap, 0, 0, w, h, 32, 0); |
| if (xi->bits_per_pixel == 16) { // convert 8 bpp ==> 16 bpp |
| ushort *p2; |
| int p2inc = xi->bytes_per_line/sizeof(ushort); |
| ushort *newerbits = (ushort *)malloc(xi->bytes_per_line * h); |
| Q_CHECK_PTR(newerbits); |
| if (!newerbits) // no memory |
| return; |
| uchar* p = newbits; |
| for (int y = 0; y < h; y++) { // OOPS: Do right byte order!! |
| p2 = newerbits + p2inc*y; |
| for (int x = 0; x < w; x++) |
| *p2++ = *p++; |
| } |
| free(newbits); |
| newbits = (uchar *)newerbits; |
| } else if (xi->bits_per_pixel != 8) { |
| qWarning("QPixmap::fromImage: Display not supported " |
| "(bpp=%d)", xi->bits_per_pixel); |
| } |
| xi->data = (char *)newbits; |
| } |
| |
| hd = XCreatePixmap(dpy, |
| RootWindow(dpy, xinfo.screen()), |
| w, h, dd); |
| |
| GC gc = XCreateGC(dpy, hd, 0, 0); |
| XPutImage(dpy, hd, gc, xi, 0, 0, 0, 0, w, h); |
| XFreeGC(dpy, gc); |
| |
| qSafeXDestroyImage(xi); |
| d = dd; |
| |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| XRenderPictFormat *format = d == 1 |
| ? XRenderFindStandardFormat(dpy, PictStandardA1) |
| : XRenderFindVisualFormat(dpy, (Visual *)xinfo.visual()); |
| picture = XRenderCreatePicture(dpy, hd, format, 0, 0); |
| } |
| #endif |
| |
| if (alphaCheck.hasAlpha()) { |
| QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags)); |
| setMask(m); |
| } |
| } |
| |
| void QX11PlatformPixmap::copy(const QPlatformPixmap *data, const QRect &rect) |
| { |
| if (data->pixelType() == BitmapType) { |
| fromImage(data->toImage().copy(rect), Qt::AutoColor); |
| return; |
| } |
| |
| const QX11PlatformPixmap *x11Data = static_cast<const QX11PlatformPixmap*>(data); |
| |
| setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); |
| |
| flags &= ~Uninitialized; |
| xinfo = x11Data->xinfo; |
| d = x11Data->d; |
| w = rect.width(); |
| h = rect.height(); |
| is_null = (w <= 0 || h <= 0); |
| hd = XCreatePixmap(xinfo.display(), |
| RootWindow(xinfo.display(), x11Data->xinfo.screen()), |
| w, h, d); |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| XRenderPictFormat *format = d == 32 |
| ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32) |
| : XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual()); |
| picture = XRenderCreatePicture(xinfo.display(), hd, format, 0, 0); |
| } |
| #endif // QT_CONFIG(xrender) |
| if (x11Data->x11_mask) { |
| x11_mask = XCreatePixmap(xinfo.display(), hd, w, h, 1); |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask, |
| XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0); |
| XRenderPictureAttributes attrs; |
| attrs.alpha_map = x11Data->mask_picture; |
| XRenderChangePicture(xinfo.display(), x11Data->picture, CPAlphaMap, &attrs); |
| } |
| #endif |
| } |
| |
| #if QT_CONFIG(xrender) |
| if (x11Data->picture && x11Data->d == 32) { |
| XRenderComposite(xinfo.display(), PictOpSrc, |
| x11Data->picture, 0, picture, |
| rect.x(), rect.y(), 0, 0, 0, 0, w, h); |
| } else |
| #endif |
| { |
| GC gc = XCreateGC(xinfo.display(), hd, 0, 0); |
| XCopyArea(xinfo.display(), x11Data->hd, hd, gc, |
| rect.x(), rect.y(), w, h, 0, 0); |
| if (x11Data->x11_mask) { |
| GC monogc = XCreateGC(xinfo.display(), x11_mask, 0, 0); |
| XCopyArea(xinfo.display(), x11Data->x11_mask, x11_mask, monogc, |
| rect.x(), rect.y(), w, h, 0, 0); |
| XFreeGC(xinfo.display(), monogc); |
| } |
| XFreeGC(xinfo.display(), gc); |
| } |
| } |
| |
| bool QX11PlatformPixmap::scroll(int dx, int dy, const QRect &rect) |
| { |
| GC gc = XCreateGC(xinfo.display(), hd, 0, 0); |
| XCopyArea(xinfo.display(), hd, hd, gc, |
| rect.left(), rect.top(), rect.width(), rect.height(), |
| rect.left() + dx, rect.top() + dy); |
| XFreeGC(xinfo.display(), gc); |
| return true; |
| } |
| |
| int QX11PlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const |
| { |
| switch (metric) { |
| case QPaintDevice::PdmDevicePixelRatio: |
| return devicePixelRatio(); |
| break; |
| case QPaintDevice::PdmDevicePixelRatioScaled: |
| return devicePixelRatio() * QPaintDevice::devicePixelRatioFScale(); |
| break; |
| case QPaintDevice::PdmWidth: |
| return w; |
| case QPaintDevice::PdmHeight: |
| return h; |
| case QPaintDevice::PdmNumColors: |
| return 1 << d; |
| case QPaintDevice::PdmDepth: |
| return d; |
| case QPaintDevice::PdmWidthMM: { |
| const int screen = xinfo.screen(); |
| const int mm = DisplayWidthMM(xinfo.display(), screen) * w |
| / DisplayWidth(xinfo.display(), screen); |
| return mm; |
| } |
| case QPaintDevice::PdmHeightMM: { |
| const int screen = xinfo.screen(); |
| const int mm = (DisplayHeightMM(xinfo.display(), screen) * h) |
| / DisplayHeight(xinfo.display(), screen); |
| return mm; |
| } |
| case QPaintDevice::PdmDpiX: |
| case QPaintDevice::PdmPhysicalDpiX: |
| return QXcbX11Info::appDpiX(xinfo.screen()); |
| case QPaintDevice::PdmDpiY: |
| case QPaintDevice::PdmPhysicalDpiY: |
| return QXcbX11Info::appDpiY(xinfo.screen()); |
| default: |
| qWarning("QX11PlatformPixmap::metric(): Invalid metric"); |
| return 0; |
| } |
| } |
| |
| void QX11PlatformPixmap::fill(const QColor &fillColor) |
| { |
| if (fillColor.alpha() != 255) { |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| if (!picture || d != 32) |
| convertToARGB32(/*preserveContents = */false); |
| |
| ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor); |
| XRenderComposite(xinfo.display(), PictOpSrc, src, 0, picture, |
| 0, 0, width(), height(), |
| 0, 0, width(), height()); |
| } else |
| #endif |
| { |
| QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied); |
| im.fill(PREMUL(fillColor.rgba())); |
| release(); |
| fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither); |
| } |
| return; |
| } |
| |
| GC gc = XCreateGC(xinfo.display(), hd, 0, 0); |
| if (depth() == 1) { |
| XSetForeground(xinfo.display(), gc, qGray(fillColor.rgb()) > 127 ? 0 : 1); |
| } else if (X11->use_xrender && d >= 24) { |
| XSetForeground(xinfo.display(), gc, fillColor.rgba()); |
| } else { |
| XSetForeground(xinfo.display(), gc, |
| QXcbColormap::instance(xinfo.screen()).pixel(fillColor)); |
| } |
| XFillRectangle(xinfo.display(), hd, gc, 0, 0, width(), height()); |
| XFreeGC(xinfo.display(), gc); |
| } |
| |
| QBitmap QX11PlatformPixmap::mask() const |
| { |
| QBitmap mask; |
| #if QT_CONFIG(xrender) |
| if (picture && d == 32) { |
| // #### slow - there must be a better way.. |
| mask = QBitmap::fromImage(toImage().createAlphaMask()); |
| } else |
| #endif |
| if (d == 1) { |
| QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this); |
| mask = QPixmap(that); |
| } else { |
| mask = mask_to_bitmap(xinfo.screen()); |
| } |
| return mask; |
| } |
| |
| void QX11PlatformPixmap::setMask(const QBitmap &newmask) |
| { |
| if (newmask.isNull()) { // clear mask |
| #if QT_CONFIG(xrender) |
| if (picture && d == 32) { |
| QX11PlatformPixmap newData(pixelType()); |
| newData.resize(w, h); |
| newData.fill(Qt::black); |
| XRenderComposite(xinfo.display(), PictOpOver, |
| picture, 0, newData.picture, |
| 0, 0, 0, 0, 0, 0, w, h); |
| release(); |
| *this = newData; |
| // the new QX11PlatformPixmap object isn't referenced yet, so |
| // ref it |
| ref.ref(); |
| |
| // the below is to make sure the QX11PlatformPixmap destructor |
| // doesn't delete our newly created render picture |
| newData.hd = 0; |
| newData.x11_mask = 0; |
| newData.picture = 0; |
| newData.mask_picture = 0; |
| newData.hd2 = 0; |
| } else |
| #endif |
| if (x11_mask) { |
| #if QT_CONFIG(xrender) |
| if (picture) { |
| XRenderPictureAttributes attrs; |
| attrs.alpha_map = 0; |
| XRenderChangePicture(xinfo.display(), picture, CPAlphaMap, |
| &attrs); |
| } |
| if (mask_picture) |
| XRenderFreePicture(xinfo.display(), mask_picture); |
| mask_picture = 0; |
| #endif |
| XFreePixmap(xinfo.display(), x11_mask); |
| x11_mask = 0; |
| } |
| return; |
| } |
| |
| #if QT_CONFIG(xrender) |
| if (picture && d == 32) { |
| XRenderComposite(xinfo.display(), PictOpSrc, |
| picture, qt_x11Pixmap(newmask)->x11PictureHandle(), |
| picture, 0, 0, 0, 0, 0, 0, w, h); |
| } else |
| #endif |
| if (depth() == 1) { |
| XGCValues vals; |
| vals.function = GXand; |
| GC gc = XCreateGC(xinfo.display(), hd, GCFunction, &vals); |
| XCopyArea(xinfo.display(), qt_x11Pixmap(newmask)->handle(), hd, gc, 0, 0, |
| width(), height(), 0, 0); |
| XFreeGC(xinfo.display(), gc); |
| } else { |
| // ##### should or the masks together |
| if (x11_mask) { |
| XFreePixmap(xinfo.display(), x11_mask); |
| #if QT_CONFIG(xrender) |
| if (mask_picture) |
| XRenderFreePicture(xinfo.display(), mask_picture); |
| #endif |
| } |
| x11_mask = QX11PlatformPixmap::bitmap_to_mask(newmask, xinfo.screen()); |
| #if QT_CONFIG(xrender) |
| if (picture) { |
| mask_picture = XRenderCreatePicture(xinfo.display(), x11_mask, |
| XRenderFindStandardFormat(xinfo.display(), PictStandardA1), 0, 0); |
| XRenderPictureAttributes attrs; |
| attrs.alpha_map = mask_picture; |
| XRenderChangePicture(xinfo.display(), picture, CPAlphaMap, &attrs); |
| } |
| #endif |
| } |
| } |
| |
| bool QX11PlatformPixmap::hasAlphaChannel() const |
| { |
| if (picture && d == 32) |
| return true; |
| |
| if (x11_mask && d == 1) |
| return true; |
| |
| return false; |
| } |
| |
| QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::TransformationMode mode) const |
| { |
| if (mode == Qt::SmoothTransformation || transform.type() >= QTransform::TxProject) { |
| QImage image = toImage(); |
| return QPixmap::fromImage(image.transformed(transform, mode)); |
| } |
| |
| uint w = 0; |
| uint h = 0; // size of target pixmap |
| uint ws, hs; // size of source pixmap |
| uchar *dptr; // data in target pixmap |
| uint dbpl, dbytes; // bytes per line/bytes total |
| uchar *sptr; // data in original pixmap |
| int sbpl; // bytes per line in original |
| int bpp; // bits per pixel |
| bool depth1 = depth() == 1; |
| Display *dpy = xinfo.display(); |
| |
| ws = width(); |
| hs = height(); |
| |
| QTransform mat(transform.m11(), transform.m12(), transform.m13(), |
| transform.m21(), transform.m22(), transform.m23(), |
| 0., 0., 1); |
| bool complex_xform = false; |
| qreal scaledWidth; |
| qreal scaledHeight; |
| |
| if (mat.type() <= QTransform::TxScale) { |
| scaledHeight = qAbs(mat.m22()) * hs + 0.9999; |
| scaledWidth = qAbs(mat.m11()) * ws + 0.9999; |
| h = qAbs(int(scaledHeight)); |
| w = qAbs(int(scaledWidth)); |
| } else { // rotation or shearing |
| QPolygonF a(QRectF(0, 0, ws, hs)); |
| a = mat.map(a); |
| QRect r = a.boundingRect().toAlignedRect(); |
| w = r.width(); |
| h = r.height(); |
| scaledWidth = w; |
| scaledHeight = h; |
| complex_xform = true; |
| } |
| mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix |
| |
| bool invertible; |
| mat = mat.inverted(&invertible); // invert matrix |
| |
| if (h == 0 || w == 0 || !invertible |
| || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 ) |
| // error, return null pixmap |
| return QPixmap(); |
| |
| XImage *xi = XGetImage(xinfo.display(), handle(), 0, 0, ws, hs, AllPlanes, |
| depth1 ? XYPixmap : ZPixmap); |
| |
| if (!xi) |
| return QPixmap(); |
| |
| sbpl = xi->bytes_per_line; |
| sptr = (uchar *)xi->data; |
| bpp = xi->bits_per_pixel; |
| |
| if (depth1) |
| dbpl = (w+7)/8; |
| else |
| dbpl = ((w*bpp+31)/32)*4; |
| dbytes = dbpl*h; |
| |
| dptr = (uchar *)malloc(dbytes); // create buffer for bits |
| Q_CHECK_PTR(dptr); |
| if (depth1) // fill with zeros |
| memset(dptr, 0, dbytes); |
| else if (bpp == 8) // fill with background color |
| memset(dptr, WhitePixel(xinfo.display(), xinfo.screen()), dbytes); |
| else |
| memset(dptr, 0, dbytes); |
| |
| // #define QT_DEBUG_XIMAGE |
| #if defined(QT_DEBUG_XIMAGE) |
| qDebug("----IMAGE--INFO--------------"); |
| qDebug("width............. %d", xi->width); |
| qDebug("height............ %d", xi->height); |
| qDebug("xoffset........... %d", xi->xoffset); |
| qDebug("format............ %d", xi->format); |
| qDebug("byte order........ %d", xi->byte_order); |
| qDebug("bitmap unit....... %d", xi->bitmap_unit); |
| qDebug("bitmap bit order.. %d", xi->bitmap_bit_order); |
| qDebug("depth............. %d", xi->depth); |
| qDebug("bytes per line.... %d", xi->bytes_per_line); |
| qDebug("bits per pixel.... %d", xi->bits_per_pixel); |
| #endif |
| |
| int type; |
| if (xi->bitmap_bit_order == MSBFirst) |
| type = QT_XFORM_TYPE_MSBFIRST; |
| else |
| type = QT_XFORM_TYPE_LSBFIRST; |
| int xbpl, p_inc; |
| if (depth1) { |
| xbpl = (w+7)/8; |
| p_inc = dbpl - xbpl; |
| } else { |
| xbpl = (w*bpp)/8; |
| p_inc = dbpl - xbpl; |
| } |
| |
| if (!qt_xForm_helper(mat, xi->xoffset, type, bpp, dptr, xbpl, p_inc, h, sptr, sbpl, ws, hs)){ |
| qWarning("QPixmap::transform: display not supported (bpp=%d)",bpp); |
| QPixmap pm; |
| free(dptr); |
| return pm; |
| } |
| |
| qSafeXDestroyImage(xi); |
| |
| if (depth1) { // mono bitmap |
| QBitmap bm = QBitmap::fromData(QSize(w, h), dptr, |
| BitmapBitOrder(xinfo.display()) == MSBFirst |
| ? QImage::Format_Mono |
| : QImage::Format_MonoLSB); |
| free(dptr); |
| return bm; |
| } else { // color pixmap |
| QX11PlatformPixmap *x11Data = new QX11PlatformPixmap(QPlatformPixmap::PixmapType); |
| QPixmap pm(x11Data); |
| x11Data->flags &= ~QX11PlatformPixmap::Uninitialized; |
| x11Data->xinfo = xinfo; |
| x11Data->d = d; |
| x11Data->w = w; |
| x11Data->h = h; |
| x11Data->is_null = (w <= 0 || h <= 0); |
| x11Data->hd = XCreatePixmap(xinfo.display(), |
| RootWindow(xinfo.display(), xinfo.screen()), |
| w, h, d); |
| x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); |
| |
| #if QT_CONFIG(xrender) |
| if (X11->use_xrender) { |
| XRenderPictFormat *format = x11Data->d == 32 |
| ? XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32) |
| : XRenderFindVisualFormat(xinfo.display(), (Visual *) x11Data->xinfo.visual()); |
| x11Data->picture = XRenderCreatePicture(xinfo.display(), x11Data->hd, format, 0, 0); |
| } |
| #endif // QT_CONFIG(xrender) |
| |
| GC gc = XCreateGC(xinfo.display(), x11Data->hd, 0, 0); |
| xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(), |
| x11Data->d, |
| ZPixmap, 0, (char *)dptr, w, h, 32, 0); |
| XPutImage(dpy, qt_x11Pixmap(pm)->handle(), gc, xi, 0, 0, 0, 0, w, h); |
| qSafeXDestroyImage(xi); |
| XFreeGC(xinfo.display(), gc); |
| |
| if (x11_mask) { // xform mask, too |
| pm.setMask(mask_to_bitmap(xinfo.screen()).transformed(transform)); |
| } else if (d != 32 && complex_xform) { // need a mask! |
| QBitmap mask(ws, hs); |
| mask.fill(Qt::color1); |
| pm.setMask(mask.transformed(transform)); |
| } |
| return pm; |
| } |
| } |
| |
| QImage QX11PlatformPixmap::toImage() const |
| { |
| return toImage(QRect(0, 0, w, h)); |
| } |
| |
| QImage QX11PlatformPixmap::toImage(const QRect &rect) const |
| { |
| Window root_return; |
| int x_return; |
| int y_return; |
| unsigned int width_return; |
| unsigned int height_return; |
| unsigned int border_width_return; |
| unsigned int depth_return; |
| |
| XGetGeometry(xinfo.display(), hd, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return); |
| |
| QXImageWrapper xiWrapper; |
| xiWrapper.xi = XGetImage(xinfo.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 (!x11_mask && canTakeQImageFromXImage(xiWrapper)) |
| return takeQImageFromXImage(xiWrapper); |
| |
| QImage image = toImage(xiWrapper, rect); |
| qSafeXDestroyImage(xiWrapper.xi); |
| return image; |
| } |
| |
| #if QT_CONFIG(xrender) |
| static XRenderPictFormat *qt_renderformat_for_depth(const QXcbX11Info &xinfo, int depth) |
| { |
| if (depth == 1) |
| return XRenderFindStandardFormat(xinfo.display(), PictStandardA1); |
| else if (depth == 32) |
| return XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32); |
| else |
| return XRenderFindVisualFormat(xinfo.display(), (Visual *)xinfo.visual()); |
| } |
| #endif |
| |
| Q_GLOBAL_STATIC(QX11PaintEngine, qt_x11_paintengine) |
| |
| QPaintEngine *QX11PlatformPixmap::paintEngine() const |
| { |
| QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this); |
| |
| if ((flags & Readonly)/* && share_mode == QPixmap::ImplicitlyShared*/) { |
| // if someone wants to draw onto us, copy the shared contents |
| // and turn it into a fully fledged QPixmap |
| ::Pixmap hd_copy = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()), |
| w, h, d); |
| #if QT_CONFIG(xrender) |
| if (picture && d == 32) { |
| XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d); |
| ::Picture picture_copy = XRenderCreatePicture(xinfo.display(), |
| hd_copy, format, |
| 0, 0); |
| |
| XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, picture_copy, |
| 0, 0, 0, 0, 0, 0, w, h); |
| XRenderFreePicture(xinfo.display(), picture); |
| that->picture = picture_copy; |
| } else |
| #endif |
| { |
| GC gc = XCreateGC(xinfo.display(), hd_copy, 0, 0); |
| XCopyArea(xinfo.display(), hd, hd_copy, gc, 0, 0, w, h, 0, 0); |
| XFreeGC(xinfo.display(), gc); |
| } |
| that->hd = hd_copy; |
| that->flags &= ~QX11PlatformPixmap::Readonly; |
| } |
| |
| if (qt_x11_paintengine->isActive()) { |
| if (!that->pengine) |
| that->pengine = new QX11PaintEngine; |
| |
| return that->pengine; |
| } |
| |
| return qt_x11_paintengine(); |
| } |
| |
| qreal QX11PlatformPixmap::devicePixelRatio() const |
| { |
| return dpr; |
| } |
| |
| void QX11PlatformPixmap::setDevicePixelRatio(qreal scaleFactor) |
| { |
| dpr = scaleFactor; |
| } |
| |
| Pixmap QX11PlatformPixmap::x11ConvertToDefaultDepth() |
| { |
| #if QT_CONFIG(xrender) |
| if (d == xinfo.appDepth() || !X11->use_xrender) |
| return hd; |
| if (!hd2) { |
| hd2 = XCreatePixmap(xinfo.display(), hd, w, h, xinfo.appDepth()); |
| XRenderPictFormat *format = XRenderFindVisualFormat(xinfo.display(), |
| (Visual*) xinfo.visual()); |
| Picture pic = XRenderCreatePicture(xinfo.display(), hd2, format, 0, 0); |
| XRenderComposite(xinfo.display(), PictOpSrc, picture, |
| XNone, pic, 0, 0, 0, 0, 0, 0, w, h); |
| XRenderFreePicture(xinfo.display(), pic); |
| } |
| return hd2; |
| #else |
| return hd; |
| #endif |
| } |
| |
| XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image) |
| { |
| QImage img = image.convertToFormat(QImage::Format_MonoLSB); |
| const QRgb c0 = QColor(Qt::black).rgb(); |
| const QRgb c1 = QColor(Qt::white).rgb(); |
| if (img.color(0) == c0 && img.color(1) == c1) { |
| img.invertPixels(); |
| img.setColor(0, c1); |
| img.setColor(1, c0); |
| } |
| |
| char *bits; |
| uchar *tmp_bits; |
| int w = img.width(); |
| int h = img.height(); |
| int bpl = (w + 7) / 8; |
| int ibpl = img.bytesPerLine(); |
| if (bpl != ibpl) { |
| tmp_bits = new uchar[bpl*h]; |
| bits = (char *)tmp_bits; |
| uchar *p, *b; |
| int y; |
| b = tmp_bits; |
| p = img.scanLine(0); |
| for (y = 0; y < h; y++) { |
| memcpy(b, p, bpl); |
| b += bpl; |
| p += ibpl; |
| } |
| } else { |
| bits = (char *)img.bits(); |
| tmp_bits = 0; |
| } |
| XID hd = XCreateBitmapFromData(QXcbX11Info::display(), |
| QXcbX11Info::appRootWindow(), |
| bits, w, h); |
| if (tmp_bits) // Avoid purify complaint |
| delete [] tmp_bits; |
| return hd; |
| } |
| |
| bool QX11PlatformPixmap::isBackingStore() const |
| { |
| return (flags & IsBackingStore); |
| } |
| |
| void QX11PlatformPixmap::setIsBackingStore(bool on) |
| { |
| if (on) |
| flags |= IsBackingStore; |
| else { |
| flags &= ~IsBackingStore; |
| } |
| } |
| |
| #if QT_CONFIG(xrender) |
| void QX11PlatformPixmap::convertToARGB32(bool preserveContents) |
| { |
| if (!X11->use_xrender) |
| return; |
| |
| // Q_ASSERT(count == 1); |
| if ((flags & Readonly)/* && share_mode == QPixmap::ExplicitlyShared*/) |
| return; |
| |
| Pixmap pm = XCreatePixmap(xinfo.display(), RootWindow(xinfo.display(), xinfo.screen()), |
| w, h, 32); |
| Picture p = XRenderCreatePicture(xinfo.display(), pm, |
| XRenderFindStandardFormat(xinfo.display(), PictStandardARGB32), 0, 0); |
| if (picture) { |
| if (preserveContents) |
| XRenderComposite(xinfo.display(), PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h); |
| if (!(flags & Readonly)) |
| XRenderFreePicture(xinfo.display(), picture); |
| } |
| if (hd && !(flags & Readonly)) |
| XFreePixmap(xinfo.display(), hd); |
| if (x11_mask) { |
| XFreePixmap(xinfo.display(), x11_mask); |
| if (mask_picture) |
| XRenderFreePicture(xinfo.display(), mask_picture); |
| x11_mask = 0; |
| mask_picture = 0; |
| } |
| hd = pm; |
| picture = p; |
| |
| d = 32; |
| xinfo.setDepth(32); |
| |
| XVisualInfo visinfo; |
| if (XMatchVisualInfo(xinfo.display(), xinfo.screen(), 32, TrueColor, &visinfo)) |
| xinfo.setVisual(visinfo.visual); |
| } |
| #endif |
| |
| void QX11PlatformPixmap::release() |
| { |
| delete pengine; |
| pengine = 0; |
| |
| if (/*!X11*/ QCoreApplication::closingDown()) { |
| // At this point, the X server will already have freed our resources, |
| // so there is nothing to do. |
| return; |
| } |
| |
| if (x11_mask) { |
| #if QT_CONFIG(xrender) |
| if (mask_picture) |
| XRenderFreePicture(xinfo.display(), mask_picture); |
| mask_picture = 0; |
| #endif |
| XFreePixmap(xinfo.display(), x11_mask); |
| x11_mask = 0; |
| } |
| |
| if (hd) { |
| #if QT_CONFIG(xrender) |
| if (picture) { |
| XRenderFreePicture(xinfo.display(), picture); |
| picture = 0; |
| } |
| #endif // QT_CONFIG(xrender) |
| |
| if (hd2) { |
| XFreePixmap(xinfo.display(), hd2); |
| hd2 = 0; |
| } |
| if (!(flags & Readonly)) |
| XFreePixmap(xinfo.display(), hd); |
| hd = 0; |
| } |
| } |
| |
| QImage QX11PlatformPixmap::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; |
| |
| QImage::Format format = QImage::Format_Mono; |
| if (d > 1 && d <= 8) { |
| d = 8; |
| format = QImage::Format_Indexed8; |
| } |
| // we could run into the situation where d == 8 AND trucol is true, which can |
| // cause problems when converting to and from images. in this case, always treat |
| // the depth as 32... |
| if (d > 8 || trucol) { |
| d = 32; |
| format = QImage::Format_RGB32; |
| } |
| |
| 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(xi->width, xi->height, format); |
| image.setDevicePixelRatio(devicePixelRatio()); |
| if (image.isNull()) // could not create image |
| return image; |
| |
| QImage alpha; |
| if (x11_mask) { |
| if (rect.contains(QRect(0, 0, w, h))) |
| alpha = mask().toImage(); |
| else |
| alpha = mask().toImage().copy(rect); |
| } |
| bool ale = alpha.format() == QImage::Format_MonoLSB; |
| |
| if (trucol) { // truecolor |
| const uint red_mask = (uint)visual->red_mask; |
| const uint green_mask = (uint)visual->green_mask; |
| const uint blue_mask = (uint)visual->blue_mask; |
| const int red_shift = highest_bit(red_mask) - 7; |
| const int green_shift = highest_bit(green_mask) - 7; |
| const int blue_shift = highest_bit(blue_mask) - 7; |
| |
| const uint red_bits = n_bits(red_mask); |
| const uint green_bits = n_bits(green_mask); |
| const uint blue_bits = n_bits(blue_mask); |
| |
| static uint red_table_bits = 0; |
| static uint green_table_bits = 0; |
| static uint blue_table_bits = 0; |
| |
| if (red_bits < 8 && red_table_bits != red_bits) { |
| build_scale_table(&red_scale_table, red_bits); |
| red_table_bits = red_bits; |
| } |
| if (blue_bits < 8 && blue_table_bits != blue_bits) { |
| build_scale_table(&blue_scale_table, blue_bits); |
| blue_table_bits = blue_bits; |
| } |
| if (green_bits < 8 && green_table_bits != green_bits) { |
| build_scale_table(&green_scale_table, green_bits); |
| green_table_bits = green_bits; |
| } |
| |
| int r, g, b; |
| |
| QRgb *dst; |
| uchar *src; |
| uint pixel; |
| int bppc = xi->bits_per_pixel; |
| |
| if (bppc > 8 && xi->byte_order == LSBFirst) |
| bppc++; |
| |
| 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 < xi->width; x++) { |
| switch (bppc) { |
| case 8: |
| pixel = *src++; |
| break; |
| case 16: // 16 bit MSB |
| pixel = src[1] | (uint)src[0] << 8; |
| src += 2; |
| break; |
| case 17: // 16 bit LSB |
| pixel = src[0] | (uint)src[1] << 8; |
| src += 2; |
| break; |
| case 24: // 24 bit MSB |
| pixel = src[2] | (uint)src[1] << 8 | (uint)src[0] << 16; |
| src += 3; |
| break; |
| case 25: // 24 bit LSB |
| pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16; |
| src += 3; |
| break; |
| case 32: // 32 bit MSB |
| pixel = src[3] | (uint)src[2] << 8 | (uint)src[1] << 16 | (uint)src[0] << 24; |
| src += 4; |
| break; |
| case 33: // 32 bit LSB |
| pixel = src[0] | (uint)src[1] << 8 | (uint)src[2] << 16 | (uint)src[3] << 24; |
| src += 4; |
| break; |
| default: // should not really happen |
| x = xi->width; // leave loop |
| y = xi->height; |
| pixel = 0; // eliminate compiler warning |
| qWarning("QPixmap::convertToImage: Invalid depth %d", bppc); |
| } |
| if (red_shift > 0) |
| r = (pixel & red_mask) >> red_shift; |
| else |
| r = (pixel & red_mask) << -red_shift; |
| if (green_shift > 0) |
| g = (pixel & green_mask) >> green_shift; |
| else |
| g = (pixel & green_mask) << -green_shift; |
| if (blue_shift > 0) |
| b = (pixel & blue_mask) >> blue_shift; |
| else |
| b = (pixel & blue_mask) << -blue_shift; |
| |
| if (red_bits < 8) |
| r = red_scale_table[r]; |
| if (green_bits < 8) |
| g = green_scale_table[g]; |
| if (blue_bits < 8) |
| b = blue_scale_table[b]; |
| |
| if (x11_mask) { |
| if (ale) { |
| *dst++ = (asrc[x >> 3] & (1 << (x & 7))) ? qRgba(r, g, b, 0xff) : 0; |
| } else { |
| *dst++ = (asrc[x >> 3] & (0x80 >> (x & 7))) ? qRgba(r, g, b, 0xff) : 0; |
| } |
| } else { |
| *dst++ = qRgb(r, g, b); |
| } |
| } |
| } |
| } else if (xi->bits_per_pixel == d) { // compatible depth |
| char *xidata = xi->data; // copy each scanline |
| int bpl = qMin(int(image.bytesPerLine()),xi->bytes_per_line); |
| for (int y=0; y<xi->height; y++) { |
| memcpy(image.scanLine(y), xidata, bpl); |
| xidata += xi->bytes_per_line; |
| } |
| } else { |
| /* Typically 2 or 4 bits display depth */ |
| qWarning("QPixmap::convertToImage: Display not supported (bpp=%d)", |
| xi->bits_per_pixel); |
| return QImage(); |
| } |
| |
| if (d == 1) { // bitmap |
| image.setColorCount(2); |
| image.setColor(0, qRgb(255,255,255)); |
| image.setColor(1, qRgb(0,0,0)); |
| } else if (!trucol) { // pixmap with colormap |
| uchar *p; |
| uchar *end; |
| uchar use[256]; // pixel-in-use table |
| uchar pix[256]; // pixel translation table |
| int ncols, bpl; |
| memset(use, 0, 256); |
| memset(pix, 0, 256); |
| bpl = image.bytesPerLine(); |
| |
| if (x11_mask) { // which pixels are used? |
| for (int i = 0; i < xi->height; i++) { |
| uchar* asrc = alpha.scanLine(i); |
| p = image.scanLine(i); |
| if (ale) { |
| 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 < xi->width; x++) { |
| if (asrc[x >> 3] & (0x80 >> (x & 7))) |
| use[*p] = 1; |
| ++p; |
| } |
| } |
| } |
| } else { |
| for (int i = 0; i < xi->height; i++) { |
| p = image.scanLine(i); |
| end = p + bpl; |
| while (p < end) |
| use[*p++] = 1; |
| } |
| } |
| ncols = 0; |
| for (int i = 0; i < 256; i++) { // build translation table |
| if (use[i]) |
| pix[i] = ncols++; |
| } |
| for (int i = 0; i < xi->height; i++) { // translate pixels |
| p = image.scanLine(i); |
| end = p + bpl; |
| while (p < end) { |
| *p = pix[*p]; |
| p++; |
| } |
| } |
| if (x11_mask) { |
| int trans; |
| if (ncols < 256) { |
| trans = ncols++; |
| image.setColorCount(ncols); // create color table |
| image.setColor(trans, 0x00000000); |
| } else { |
| image.setColorCount(ncols); // create color table |
| // oh dear... no spare "transparent" pixel. |
| // use first pixel in image (as good as any). |
| trans = image.scanLine(0)[0]; |
| } |
| for (int i = 0; i < xi->height; i++) { |
| uchar* asrc = alpha.scanLine(i); |
| p = image.scanLine(i); |
| if (ale) { |
| for (int x = 0; x < xi->width; x++) { |
| if (!(asrc[x >> 3] & (1 << (x & 7)))) |
| *p = trans; |
| ++p; |
| } |
| } else { |
| for (int x = 0; x < xi->width; x++) { |
| if (!(asrc[x >> 3] & (1 << (7 -(x & 7))))) |
| *p = trans; |
| ++p; |
| } |
| } |
| } |
| } else { |
| image.setColorCount(ncols); // create color table |
| } |
| QVector<QColor> colors = QXcbColormap::instance(xinfo.screen()).colormap(); |
| int j = 0; |
| for (int i=0; i<colors.size(); i++) { // translate pixels |
| if (use[i]) |
| image.setColor(j++, 0xff000000 | colors.at(i).rgb()); |
| } |
| } |
| |
| return image; |
| } |
| |
| QT_END_NAMESPACE |