/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // Uncomment the next line to enable the MIT Shared Memory extension // // WARNING: This has some problems: // // 1. Consumes a 800x600 pixmap // 2. Qt does not handle the ShmCompletion message, so you will // get strange effects if you xForm() repeatedly. // // #define QT_MITSHM #if defined(Q_OS_WIN32) && defined(QT_MITSHM) #undef QT_MITSHM #endif #include "qplatformdefs.h" #include "qdebug.h" #include "qiodevice.h" #include "qpixmap_x11_p.h" #include "qbitmap.h" #include "qcolormap.h" #include "qimage.h" #include "qmatrix.h" #include "qapplication.h" #include #include #include "qx11info_x11.h" #include #include #include #include #if defined(Q_CC_MIPS) # define for if(0){}else for #endif QT_BEGIN_NAMESPACE QPixmap qt_toX11Pixmap(const QImage &image) { QPixmapData *data = new QX11PixmapData(image.depth() == 1 ? QPixmapData::BitmapType : QPixmapData::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() == QPixmapData::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 QX11PixmapData::mask_to_bitmap(int screen) const { if (!x11_mask) return QBitmap(); QPixmap::x11SetDefaultScreen(screen); QBitmap bm(w, h); GC gc = XCreateGC(X11->display, bm.handle(), 0, 0); XCopyArea(X11->display, x11_mask, bm.handle(), gc, 0, 0, bm.data->width(), bm.data->height(), 0, 0); XFreeGC(X11->display, gc); return bm; } Qt::HANDLE QX11PixmapData::bitmap_to_mask(const QBitmap &bitmap, int screen) { if (bitmap.isNull()) return 0; QBitmap bm = bitmap; bm.x11SetScreen(screen); Pixmap mask = XCreatePixmap(X11->display, RootWindow(X11->display, screen), bm.data->width(), bm.data->height(), 1); GC gc = XCreateGC(X11->display, mask, 0, 0); XCopyArea(X11->display, bm.handle(), mask, gc, 0, 0, bm.data->width(), bm.data->height(), 0, 0); XFreeGC(X11->display, gc); return mask; } /***************************************************************************** MIT Shared Memory Extension support: makes xForm noticeably (~20%) faster. *****************************************************************************/ #if defined(QT_MITSHM) static bool xshminit = false; static XShmSegmentInfo xshminfo; static XImage *xshmimg = 0; static Pixmap xshmpm = 0; static void qt_cleanup_mitshm() { if (xshmimg == 0) return; Display *dpy = QX11Info::appDisplay(); if (xshmpm) { XFreePixmap(dpy, xshmpm); xshmpm = 0; } XShmDetach(dpy, &xshminfo); xshmimg->data = 0; qSafeXDestroyImage(xshmimg); xshmimg = 0; shmdt(xshminfo.shmaddr); shmctl(xshminfo.shmid, IPC_RMID, 0); } static bool qt_create_mitshm_buffer(const QPaintDevice* dev, int w, int h) { static int major, minor; static Bool pixmaps_ok; Display *dpy = dev->data->xinfo->display(); int dd = dev->x11Depth(); Visual *vis = (Visual*)dev->x11Visual(); if (xshminit) { qt_cleanup_mitshm(); } else { if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps_ok)) return false; // MIT Shm not supported qAddPostRoutine(qt_cleanup_mitshm); xshminit = true; } xshmimg = XShmCreateImage(dpy, vis, dd, ZPixmap, 0, &xshminfo, w, h); if (!xshmimg) return false; bool ok; xshminfo.shmid = shmget(IPC_PRIVATE, xshmimg->bytes_per_line * xshmimg->height, IPC_CREAT | 0777); ok = xshminfo.shmid != -1; if (ok) { xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0); xshminfo.shmaddr = xshmimg->data; ok = (xshminfo.shmaddr != (char*)-1); } xshminfo.readOnly = false; if (ok) ok = XShmAttach(dpy, &xshminfo); if (!ok) { qSafeXDestroyImage(xshmimg); xshmimg = 0; if (xshminfo.shmaddr) shmdt(xshminfo.shmaddr); if (xshminfo.shmid != -1) shmctl(xshminfo.shmid, IPC_RMID, 0); return false; } if (pixmaps_ok) xshmpm = XShmCreatePixmap(dpy, DefaultRootWindow(dpy), xshmimg->data, &xshminfo, w, h, dd); return true; } #else // If extern, need a dummy. // // static bool qt_create_mitshm_buffer(QPaintDevice*, int, int) // { // return false; // } #endif // QT_MITSHM /***************************************************************************** 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; } // Returns position of lowest set bit in 'v' as an integer (0-31), or -1 static int lowest_bit(uint v) { int i; ulong lb; lb = 1; for (i=0; ((v & lb) == 0) && i<32; i++, lb<<=1) {} return i==32 ? -1 : 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; /***************************************************************************** QPixmap member functions *****************************************************************************/ QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; QX11PixmapData::QX11PixmapData(PixelType type) : QPixmapData(type, X11Class), gl_surface(0), hd(0), flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0), share_mode(QPixmap::ImplicitlyShared), pengine(0) { } QPixmapData *QX11PixmapData::createCompatiblePixmapData() const { return new QX11PixmapData(pixelType()); } void QX11PixmapData::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()) { QX11InfoData* xd = xinfo.getX11Data(true); xd->screen = defaultScreen; xd->depth = QX11Info::appDepth(xd->screen); xd->cells = QX11Info::appCells(xd->screen); xd->colormap = QX11Info::appColormap(xd->screen); xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen); xd->visual = (Visual *)QX11Info::appVisual(xd->screen); xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen); xinfo.setX11Data(xd); } 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 = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, d); #ifndef QT_NO_XRENDER if (X11->use_xrender) { XRenderPictFormat *format = d == 1 ? XRenderFindStandardFormat(X11->display, PictStandardA1) : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual()); picture = XRenderCreatePicture(X11->display, hd, format, 0, 0); } #endif // QT_NO_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(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 QX11PixmapData::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); if (is_null) { w = h = 0; return; } if (defaultScreen >= 0 && defaultScreen != xinfo.screen()) { QX11InfoData* xd = xinfo.getX11Data(true); xd->screen = defaultScreen; xd->depth = QX11Info::appDepth(xd->screen); xd->cells = QX11Info::appCells(xd->screen); xd->colormap = QX11Info::appColormap(xd->screen); xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen); xd->visual = (Visual *)QX11Info::appVisual(xd->screen); xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen); xinfo.setX11Data(xd); } 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 || d == 16 || d == 24) { image = image.convertToFormat(QImage::Format_RGB32, flags); fromImage(image, Qt::AutoColor); return; } Display *dpy = X11->display; Visual *visual = (Visual *)xinfo.visual(); XImage *xi = 0; bool trucol = (visual->c_class >= TrueColor); int nbytes = image.byteCount(); uchar *newbits= 0; #ifndef QT_NO_XRENDER if (alphaCheck.hasXRenderAndAlpha()) { const QImage &cimage = image; d = 32; if (QX11Info::appDepth() != d) { if (xinfo.x11data) { xinfo.x11data->depth = d; } else { QX11InfoData *xd = xinfo.getX11Data(true); xd->screen = QX11Info::appScreen(); xd->depth = d; xd->cells = QX11Info::appCells(); xd->colormap = QX11Info::appColormap(); xd->defaultColormap = QX11Info::appDefaultColormap(); xd->visual = (Visual *)QX11Info::appVisual(); xd->defaultVisual = QX11Info::appDefaultVisual(); xinfo.setX11Data(xd); } } hd = (Qt::HANDLE)XCreatePixmap(dpy, RootWindow(dpy, xinfo.screen()), w, h, d); picture = XRenderCreatePicture(X11->display, hd, XRenderFindStandardFormat(X11->display, 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 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_NO_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 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; ibyte_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< thres) \ r += (1<<(8-rbits)); \ if (g <= (255-(1<<(8-gbits))) && ((g< thres) \ g += (1<<(8-gbits)); \ if (b <= (255-(1<<(8-bbits))) && ((b< 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< thres) \ r += (1<<(8-rbits)); \ int g = qGreen(*p); \ if (g <= (255-(1<<(8-gbits))) && ((g< thres) \ g += (1<<(8-gbits)); \ int b = qBlue (*p++); \ if (b <= (255-(1<<(8-bbits))) && ((b< 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; ybytes_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>3,0xf800,0x7e0,0x1f,5,6,5) *dst16++ = pixel; } ) break; case BPP16_555: CYCLE( quint16* dst16 = (quint16*)dst; for (int x=0; x>3,0x7c00,0x3e0,0x1f,5,5,5) *dst16++ = pixel; } ) break; case BPP16_MSB: // 16 bit MSB CYCLE( for (int x=0; x> 8); *dst++ = pixel; } ) break; case BPP16_LSB: // 16 bit LSB CYCLE( for (int x=0; x> 8; } ) break; default: qFatal("Logic error"); } } else { switch (mode) { case BPP8: // 8 bit CYCLE( Q_UNUSED(p); for (int x=0; x> 8) & 0xf800) | ((*p >> 5) & 0x7e0) | ((*p >> 3) & 0x1f); ++p; } ) break; case BPP16_555: CYCLE( quint16* dst16 = (quint16*)dst; for (int x=0; x> 9) & 0x7c00) | ((*p >> 6) & 0x3e0) | ((*p >> 3) & 0x1f); ++p; } ) break; case BPP16_MSB: // 16 bit MSB CYCLE( for (int x=0; x> 8); *dst++ = pixel; } ) break; case BPP16_LSB: // 16 bit LSB CYCLE( for (int x=0; x> 8; } ) break; case BPP24_888: // 24 bit MSB CYCLE( for (int x=0; x> 16; *dst++ = pixel >> 8; *dst++ = pixel; } ) break; case BPP24_LSB: // 24 bit LSB CYCLE( for (int x=0; x> 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> 24; *dst++ = pixel >> 16; *dst++ = pixel >> 8; *dst++ = pixel; } ) break; case BPP32_LSB: // 32 bit LSB CYCLE( for (int x=0; x> 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 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; juse) { 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; juse) { 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; } QColormap cmap = QColormap::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 (int 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 = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, 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; #ifndef QT_NO_XRENDER if (X11->use_xrender) { XRenderPictFormat *format = d == 1 ? XRenderFindStandardFormat(X11->display, PictStandardA1) : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual()); picture = XRenderCreatePicture(X11->display, hd, format, 0, 0); } #endif if (alphaCheck.hasAlpha()) { QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags)); setMask(m); } } Qt::HANDLE QX11PixmapData::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; } Qt::HANDLE hd = (Qt::HANDLE)XCreateBitmapFromData(X11->display, QX11Info::appRootWindow(), bits, w, h); if (tmp_bits) // Avoid purify complaint delete [] tmp_bits; return hd; } void QX11PixmapData::bitmapFromImage(const QImage &image) { w = image.width(); h = image.height(); d = 1; is_null = (w <= 0 || h <= 0); hd = createBitmapFromImage(image); #ifndef QT_NO_XRENDER if (X11->use_xrender) picture = XRenderCreatePicture(X11->display, hd, XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); #endif // QT_NO_XRENDER } void QX11PixmapData::fill(const QColor &fillColor) { if (fillColor.alpha() != 255) { #ifndef QT_NO_XRENDER if (X11->use_xrender) { if (!picture || d != 32) convertToARGB32(/*preserveContents = */false); ::Picture src = X11->getSolidFill(xinfo.screen(), fillColor); XRenderComposite(X11->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(X11->display, hd, 0, 0); if (depth() == 1) { XSetForeground(X11->display, gc, qGray(fillColor.rgb()) > 127 ? 0 : 1); } else if (X11->use_xrender && d >= 24) { XSetForeground(X11->display, gc, fillColor.rgba()); } else { XSetForeground(X11->display, gc, QColormap::instance(xinfo.screen()).pixel(fillColor)); } XFillRectangle(X11->display, hd, gc, 0, 0, width(), height()); XFreeGC(X11->display, gc); } QX11PixmapData::~QX11PixmapData() { // Cleanup hooks have to be called before the handles are freed if (is_cached) { QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(this); is_cached = false; } release(); } void QX11PixmapData::release() { delete pengine; pengine = 0; if (!X11) { // At this point, the X server will already have freed our resources, // so there is nothing to do. return; } if (x11_mask) { #ifndef QT_NO_XRENDER if (mask_picture) XRenderFreePicture(X11->display, mask_picture); mask_picture = 0; #endif XFreePixmap(X11->display, x11_mask); x11_mask = 0; } if (hd) { #ifndef QT_NO_XRENDER if (picture) { XRenderFreePicture(X11->display, picture); picture = 0; } #endif // QT_NO_XRENDER if (hd2) { XFreePixmap(xinfo.display(), hd2); hd2 = 0; } if (!(flags & Readonly)) XFreePixmap(xinfo.display(), hd); hd = 0; } } QPixmap QX11PixmapData::alphaChannel() const { if (!hasAlphaChannel()) { QPixmap pm(w, h); pm.fill(Qt::white); return pm; } QImage im(toImage()); return QPixmap::fromImage(im.alphaChannel(), Qt::OrderedDither); } void QX11PixmapData::setAlphaChannel(const QPixmap &alpha) { QImage image(toImage()); image.setAlphaChannel(alpha.toImage()); release(); fromImage(image, Qt::OrderedDither | Qt::OrderedAlphaDither); } QBitmap QX11PixmapData::mask() const { QBitmap mask; #ifndef QT_NO_XRENDER if (picture && d == 32) { // #### slow - there must be a better way.. mask = QBitmap::fromImage(toImage().createAlphaMask()); } else #endif if (d == 1) { QX11PixmapData *that = const_cast(this); mask = QPixmap(that); } else { mask = mask_to_bitmap(xinfo.screen()); } return mask; } /*! Sets a mask bitmap. The \a newmask bitmap defines the clip mask for this pixmap. Every pixel in \a newmask corresponds to a pixel in this pixmap. Pixel value 1 means opaque and pixel value 0 means transparent. The mask must have the same size as this pixmap. \warning Setting the mask on a pixmap will cause any alpha channel data to be cleared. For example: \snippet doc/src/snippets/image/image.cpp 2 Now, alpha and alphacopy are visually different. Setting a null mask resets the mask. The effect of this function is undefined when the pixmap is being painted on. \sa mask(), {QPixmap#Pixmap Transformations}{Pixmap Transformations}, QBitmap */ void QX11PixmapData::setMask(const QBitmap &newmask) { if (newmask.isNull()) { // clear mask #ifndef QT_NO_XRENDER if (picture && d == 32) { QX11PixmapData newData(pixelType()); newData.resize(w, h); newData.fill(Qt::black); XRenderComposite(X11->display, PictOpOver, picture, 0, newData.picture, 0, 0, 0, 0, 0, 0, w, h); release(); *this = newData; // the new QX11PixmapData object isn't referenced yet, so // ref it ref.ref(); // the below is to make sure the QX11PixmapData 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) { #ifndef QT_NO_XRENDER if (picture) { XRenderPictureAttributes attrs; attrs.alpha_map = 0; XRenderChangePicture(X11->display, picture, CPAlphaMap, &attrs); } if (mask_picture) XRenderFreePicture(X11->display, mask_picture); mask_picture = 0; #endif XFreePixmap(X11->display, x11_mask); x11_mask = 0; } return; } #ifndef QT_NO_XRENDER if (picture && d == 32) { XRenderComposite(X11->display, PictOpSrc, picture, newmask.x11PictureHandle(), picture, 0, 0, 0, 0, 0, 0, w, h); } else #endif if (depth() == 1) { XGCValues vals; vals.function = GXand; GC gc = XCreateGC(X11->display, hd, GCFunction, &vals); XCopyArea(X11->display, newmask.handle(), hd, gc, 0, 0, width(), height(), 0, 0); XFreeGC(X11->display, gc); } else { // ##### should or the masks together if (x11_mask) { XFreePixmap(X11->display, x11_mask); #ifndef QT_NO_XRENDER if (mask_picture) XRenderFreePicture(X11->display, mask_picture); #endif } x11_mask = QX11PixmapData::bitmap_to_mask(newmask, xinfo.screen()); #ifndef QT_NO_XRENDER if (picture) { mask_picture = XRenderCreatePicture(X11->display, x11_mask, XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); XRenderPictureAttributes attrs; attrs.alpha_map = mask_picture; XRenderChangePicture(X11->display, picture, CPAlphaMap, &attrs); } #endif } } int QX11PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const { switch (metric) { 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(X11->display, screen) * w / DisplayWidth(X11->display, screen); return mm; } case QPaintDevice::PdmHeightMM: { const int screen = xinfo.screen(); const int mm = (DisplayHeightMM(X11->display, screen) * h) / DisplayHeight(X11->display, screen); return mm; } case QPaintDevice::PdmDpiX: case QPaintDevice::PdmPhysicalDpiX: return QX11Info::appDpiX(xinfo.screen()); case QPaintDevice::PdmDpiY: case QPaintDevice::PdmPhysicalDpiY: return QX11Info::appDpiY(xinfo.screen()); default: qWarning("QX11PixmapData::metric(): Invalid metric"); return 0; } } struct QXImageWrapper { XImage *xi; }; bool QX11PixmapData::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const { XImage *xi = xiWrapper.xi; // ARGB32_Premultiplied if (picture && depth() == 32) return true; Visual *visual = (Visual *)xinfo.visual(); // RGB32 if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000 && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) return true; // RGB16 if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800 && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) return true; return false; } QImage QX11PixmapData::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const { XImage *xi = xiWrapper.xi; QImage::Format format = QImage::Format_ARGB32_Premultiplied; if (depth() == 24) format = QImage::Format_RGB32; else if (depth() == 16) format = QImage::Format_RGB16; QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format); // take ownership image.data_ptr()->own_data = true; xi->data = 0; // we may have to swap the byte order if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst) || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst)) { for (int i=0; i < image.height(); i++) { if (depth() == 16) { ushort *p = (ushort*)image.scanLine(i); ushort *end = p + image.width(); while (p < end) { *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); p++; } } else { uint *p = (uint*)image.scanLine(i); uint *end = p + image.width(); while (p < end) { *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); p++; } } } } // fix-up alpha channel if (format == QImage::Format_RGB32) { QRgb *p = (QRgb *)image.bits(); for (int y = 0; y < xi->height; ++y) { for (int x = 0; x < xi->width; ++x) p[x] |= 0xff000000; p += xi->bytes_per_line / 4; } } XDestroyImage(xi); return image; } QImage QX11PixmapData::toImage(const QRect &rect) const { QXImageWrapper xiWrapper; xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(), AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap); Q_CHECK_PTR(xiWrapper.xi); if (!xiWrapper.xi) return QImage(); if (!x11_mask && canTakeQImageFromXImage(xiWrapper)) return takeQImageFromXImage(xiWrapper); QImage image = toImage(xiWrapper, rect); qSafeXDestroyImage(xiWrapper.xi); return image; } /*! Converts the pixmap to a QImage. Returns a null image if the conversion fails. If the pixmap has 1-bit depth, the returned image will also be 1 bit deep. If the pixmap has 2- to 8-bit depth, the returned image has 8-bit depth. If the pixmap has greater than 8-bit depth, the returned image has 32-bit depth. Note that for the moment, alpha masks on monochrome images are ignored. \sa fromImage(), {QImage#Image Formats}{Image Formats} */ QImage QX11PixmapData::toImage() const { return toImage(QRect(0, 0, w, h)); } QImage QX11PixmapData::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const { XImage *xi = xiWrapper.xi; int d = depth(); Visual *visual = (Visual *)xinfo.visual(); bool trucol = (visual->c_class >= TrueColor) && d > 1; 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); 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; yheight; 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 register 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 colors = QColormap::instance(xinfo.screen()).colormap(); int j = 0; for (int i=0; i= 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 = X11->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(); #if defined(QT_MITSHM) static bool try_once = true; if (try_once) { try_once = false; if (!xshminit) qt_create_mitshm_buffer(this, 800, 600); } bool use_mitshm = xshmimg && !depth1 && xshmimg->width >= w && xshmimg->height >= h; #endif XImage *xi = XGetImage(X11->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; #if defined(QT_MITSHM) if (use_mitshm) { dptr = (uchar *)xshmimg->data; uchar fillbyte = bpp == 8 ? white.pixel() : 0xff; for (int y=0; ybytes_per_line, fillbyte, dbpl); } else { #endif 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(X11->display, xinfo.screen()), dbytes); else memset(dptr, 0, dbytes); #if defined(QT_MITSHM) } #endif // #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 defined(QT_MITSHM) if (use_mitshm) p_inc = xshmimg->bytes_per_line - xbpl; #endif } 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; return pm; } qSafeXDestroyImage(xi); if (depth1) { // mono bitmap QBitmap bm = QBitmap::fromData(QSize(w, h), dptr, BitmapBitOrder(X11->display) == MSBFirst ? QImage::Format_Mono : QImage::Format_MonoLSB); free(dptr); return bm; } else { // color pixmap QX11PixmapData *x11Data = new QX11PixmapData(QPixmapData::PixmapType); QPixmap pm(x11Data); x11Data->flags &= ~QX11PixmapData::Uninitialized; x11Data->xinfo = xinfo; x11Data->d = d; x11Data->w = w; x11Data->h = h; x11Data->is_null = (w <= 0 || h <= 0); x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, d); x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); #ifndef QT_NO_XRENDER if (X11->use_xrender) { XRenderPictFormat *format = x11Data->d == 32 ? XRenderFindStandardFormat(X11->display, PictStandardARGB32) : XRenderFindVisualFormat(X11->display, (Visual *) x11Data->xinfo.visual()); x11Data->picture = XRenderCreatePicture(X11->display, x11Data->hd, format, 0, 0); } #endif // QT_NO_XRENDER GC gc = XCreateGC(X11->display, x11Data->hd, 0, 0); #if defined(QT_MITSHM) if (use_mitshm) { XCopyArea(dpy, xshmpm, x11Data->hd, gc, 0, 0, w, h, 0, 0); } else #endif { xi = XCreateImage(dpy, (Visual*)x11Data->xinfo.visual(), x11Data->d, ZPixmap, 0, (char *)dptr, w, h, 32, 0); XPutImage(dpy, pm.handle(), gc, xi, 0, 0, 0, 0, w, h); qSafeXDestroyImage(xi); } XFreeGC(X11->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; } } int QPixmap::x11SetDefaultScreen(int screen) { int old = defaultScreen; defaultScreen = screen; return old; } void QPixmap::x11SetScreen(int screen) { if (paintingActive()) { qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting"); return; } if (isNull()) return; if (data->classId() != QPixmapData::X11Class) return; if (screen < 0) screen = QX11Info::appScreen(); QX11PixmapData *x11Data = static_cast(data.data()); if (screen == x11Data->xinfo.screen()) return; // nothing to do if (isNull()) { QX11InfoData* xd = x11Data->xinfo.getX11Data(true); xd->screen = screen; xd->depth = QX11Info::appDepth(screen); xd->cells = QX11Info::appCells(screen); xd->colormap = QX11Info::appColormap(screen); xd->defaultColormap = QX11Info::appDefaultColormap(screen); xd->visual = (Visual *)QX11Info::appVisual(screen); xd->defaultVisual = QX11Info::appDefaultVisual(screen); x11Data->xinfo.setX11Data(xd); return; } #if 0 qDebug("QPixmap::x11SetScreen for %p from %d to %d. Size is %d/%d", x11Data, x11Data->xinfo.screen(), screen, width(), height()); #endif x11SetDefaultScreen(screen); *this = qt_toX11Pixmap(toImage()); } QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) { if (w == 0 || h == 0) return QPixmap(); Display *dpy = X11->display; XWindowAttributes window_attr; if (!XGetWindowAttributes(dpy, window, &window_attr)) return QPixmap(); if (w < 0) w = window_attr.width - x; if (h < 0) h = window_attr.height - y; // determine the screen int scr; for (scr = 0; scr < ScreenCount(dpy); ++scr) { if (window_attr.root == RootWindow(dpy, scr)) // found it break; } if (scr >= ScreenCount(dpy)) // sanity check return QPixmap(); // get the depth of the root window XWindowAttributes root_attr; if (!XGetWindowAttributes(dpy, window_attr.root, &root_attr)) return QPixmap(); if (window_attr.depth == root_attr.depth) { // if the depth of the specified window and the root window are the // same, grab pixels from the root window (so that we get the any // overlapping windows and window manager frames) // map x and y to the root window WId unused; if (!XTranslateCoordinates(dpy, window, window_attr.root, x, y, &x, &y, &unused)) return QPixmap(); window = window_attr.root; window_attr = root_attr; } QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType); void qt_x11_getX11InfoForWindow(QX11Info * xinfo, const XWindowAttributes &a); qt_x11_getX11InfoForWindow(&data->xinfo,window_attr); data->resize(w, h); QPixmap pm(data); data->flags &= ~QX11PixmapData::Uninitialized; pm.x11SetScreen(scr); GC gc = XCreateGC(dpy, pm.handle(), 0, 0); XSetSubwindowMode(dpy, gc, IncludeInferiors); XCopyArea(dpy, window, pm.handle(), gc, x, y, w, h, 0, 0); XFreeGC(dpy, gc); return pm; } bool QX11PixmapData::hasAlphaChannel() const { return d == 32; } const QX11Info &QPixmap::x11Info() const { if (data && data->classId() == QPixmapData::X11Class) return static_cast(data.data())->xinfo; else { static QX11Info nullX11Info; return nullX11Info; } } #if !defined(QT_NO_XRENDER) static XRenderPictFormat *qt_renderformat_for_depth(const QX11Info &xinfo, int depth) { if (depth == 1) return XRenderFindStandardFormat(X11->display, PictStandardA1); else if (depth == 32) return XRenderFindStandardFormat(X11->display, PictStandardARGB32); else return XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual()); } #endif QPaintEngine* QX11PixmapData::paintEngine() const { QX11PixmapData *that = const_cast(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(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, d); #if !defined(QT_NO_XRENDER) XRenderPictFormat *format = qt_renderformat_for_depth(xinfo, d); ::Picture picture_copy = XRenderCreatePicture(X11->display, hd_copy, format, 0, 0); if (picture && d == 32) { XRenderComposite(X11->display, PictOpSrc, picture, 0, picture_copy, 0, 0, 0, 0, 0, 0, w, h); XRenderFreePicture(X11->display, picture); that->picture = picture_copy; } else #endif { GC gc = XCreateGC(X11->display, hd_copy, 0, 0); XCopyArea(X11->display, hd, hd_copy, gc, 0, 0, w, h, 0, 0); XFreeGC(X11->display, gc); } that->hd = hd_copy; that->flags &= ~QX11PixmapData::Readonly; } if (!that->pengine) that->pengine = new QX11PaintEngine; return that->pengine; } Qt::HANDLE QPixmap::x11PictureHandle() const { #ifndef QT_NO_XRENDER if (data && data->classId() == QPixmapData::X11Class) return static_cast(data.data())->picture; else return 0; #else return 0; #endif // QT_NO_XRENDER } Qt::HANDLE QX11PixmapData::x11ConvertToDefaultDepth() { #ifndef QT_NO_XRENDER if (d == QX11Info::appDepth() || !X11->use_xrender) return hd; if (!hd2) { hd2 = XCreatePixmap(xinfo.display(), hd, w, h, QX11Info::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 } void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) { if (data->pixelType() == BitmapType) { fromImage(data->toImage().copy(rect), Qt::AutoColor); return; } const QX11PixmapData *x11Data = static_cast(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 = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, x11Data->xinfo.screen()), w, h, d); #ifndef QT_NO_XRENDER if (X11->use_xrender) { XRenderPictFormat *format = d == 32 ? XRenderFindStandardFormat(X11->display, PictStandardARGB32) : XRenderFindVisualFormat(X11->display, (Visual *)xinfo.visual()); picture = XRenderCreatePicture(X11->display, hd, format, 0, 0); } #endif // QT_NO_XRENDER if (x11Data->x11_mask) { x11_mask = XCreatePixmap(X11->display, hd, w, h, 1); #ifndef QT_NO_XRENDER if (X11->use_xrender) { mask_picture = XRenderCreatePicture(X11->display, x11_mask, XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); XRenderPictureAttributes attrs; attrs.alpha_map = x11Data->mask_picture; XRenderChangePicture(X11->display, x11Data->picture, CPAlphaMap, &attrs); } #endif } #if !defined(QT_NO_XRENDER) if (x11Data->picture && x11Data->d == 32) { XRenderComposite(X11->display, PictOpSrc, x11Data->picture, 0, picture, rect.x(), rect.y(), 0, 0, 0, 0, w, h); } else #endif { GC gc = XCreateGC(X11->display, hd, 0, 0); XCopyArea(X11->display, x11Data->hd, hd, gc, rect.x(), rect.y(), w, h, 0, 0); if (x11Data->x11_mask) { GC monogc = XCreateGC(X11->display, x11_mask, 0, 0); XCopyArea(X11->display, x11Data->x11_mask, x11_mask, monogc, rect.x(), rect.y(), w, h, 0, 0); XFreeGC(X11->display, monogc); } XFreeGC(X11->display, gc); } } bool QX11PixmapData::scroll(int dx, int dy, const QRect &rect) { GC gc = XCreateGC(X11->display, hd, 0, 0); XCopyArea(X11->display, hd, hd, gc, rect.left(), rect.top(), rect.width(), rect.height(), rect.left() + dx, rect.top() + dy); XFreeGC(X11->display, gc); return true; } #if !defined(QT_NO_XRENDER) void QX11PixmapData::convertToARGB32(bool preserveContents) { if (!X11->use_xrender) return; // Q_ASSERT(count == 1); if ((flags & Readonly) && share_mode == QPixmap::ExplicitlyShared) return; Pixmap pm = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, 32); Picture p = XRenderCreatePicture(X11->display, pm, XRenderFindStandardFormat(X11->display, PictStandardARGB32), 0, 0); if (picture) { if (preserveContents) XRenderComposite(X11->display, PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h); if (!(flags & Readonly)) XRenderFreePicture(X11->display, picture); } if (hd && !(flags & Readonly)) XFreePixmap(X11->display, hd); if (x11_mask) { XFreePixmap(X11->display, x11_mask); if (mask_picture) XRenderFreePicture(X11->display, mask_picture); x11_mask = 0; mask_picture = 0; } hd = pm; picture = p; d = 32; } #endif QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode) { Window root; int x; int y; uint width; uint height; uint border_width; uint depth; XWindowAttributes win_attribs; int num_screens = ScreenCount(X11->display); int screen = 0; XGetGeometry(X11->display, pixmap, &root, &x, &y, &width, &height, &border_width, &depth); XGetWindowAttributes(X11->display, root, &win_attribs); for (; screen < num_screens; ++screen) { if (win_attribs.screen == ScreenOfDisplay(X11->display, screen)) break; } QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType); data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); data->flags = QX11PixmapData::Readonly; data->share_mode = mode; data->w = width; data->h = height; data->is_null = (width <= 0 || height <= 0); data->d = depth; data->hd = pixmap; if (defaultScreen >= 0 && defaultScreen != screen) { QX11InfoData* xd = data->xinfo.getX11Data(true); xd->screen = defaultScreen; xd->depth = QX11Info::appDepth(xd->screen); xd->cells = QX11Info::appCells(xd->screen); xd->colormap = QX11Info::appColormap(xd->screen); xd->defaultColormap = QX11Info::appDefaultColormap(xd->screen); xd->visual = (Visual *)QX11Info::appVisual(xd->screen); xd->defaultVisual = QX11Info::appDefaultVisual(xd->screen); data->xinfo.setX11Data(xd); } #ifndef QT_NO_XRENDER if (X11->use_xrender) { XRenderPictFormat *format = qt_renderformat_for_depth(data->xinfo, depth); data->picture = XRenderCreatePicture(X11->display, data->hd, format, 0, 0); } #endif // QT_NO_XRENDER return QPixmap(data); } QT_END_NAMESPACE