diff options
Diffstat (limited to 'src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp | 2114 |
1 files changed, 2114 insertions, 0 deletions
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp new file mode 100644 index 0000000000..86c87e5e30 --- /dev/null +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp @@ -0,0 +1,2114 @@ +/**************************************************************************** +** +** 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; +} + +#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(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 |