diff --git a/CMakeLists.txt b/CMakeLists.txt index 36a585f43..6e460fe2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,6 +413,10 @@ if(NOT X11_Xext_FOUND) katie_config(QT_NO_XSYNC) katie_config(QT_NO_XSHAPE) endif() +if(NOT X11_Xpm_FOUND) + message(WARNING "The X11 Xpm extension was not found") + katie_config(QT_NO_IMAGEFORMAT_XPM) +endif() configure_file( ${CMAKE_SOURCE_DIR}/src/core/global/qconfig.h.cmake diff --git a/package/debian/control b/package/debian/control index d0e6c65b4..c3f458d13 100644 --- a/package/debian/control +++ b/package/debian/control @@ -8,7 +8,7 @@ Build-Depends: debhelper (>= 9~), libdeflate-dev, libgettextpo-dev, libc6-dev, libjansson-dev, libpng-dev, libcups2-dev, libfreetype6-dev, libfontconfig1-dev, libdbus-1-dev, libicu-dev, cmake, git, xserver-xorg-dev, libxinerama-dev, libxrandr-dev, libxrender-dev, - libxcursor-dev, fonts-freefont-ttf, unifdef | dpkg + libxcursor-dev, libxpm-dev, fonts-freefont-ttf, unifdef | dpkg Package: katie-runtime Architecture: amd64 arm64 armel armhf i386 mips mips64el mipsel ppc64el s390x hurd-i386 diff --git a/package/freebsd/Makefile b/package/freebsd/Makefile index 4235a8b91..f9915b0bf 100644 --- a/package/freebsd/Makefile +++ b/package/freebsd/Makefile @@ -14,7 +14,7 @@ DIST_SUBDIR = ${PORTNAME} WRKSRC = ${WRKDIR}/katie.git USES = compiler:c++11-lang pkgconfig cmake xorg desktop-file-utils -USE_XORG = x11 xinerama xrandr xrender xfixes xcursor xext +USE_XORG = x11 xinerama xrandr xrender xfixes xcursor xext xpm USE_LDCONFIG = yes USE_GETTEXT_TOOLS = build run RUN_DEPENDS = xdg-open:devel/xdg-utils \ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 1e4c39066..f3ffe62e2 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -824,7 +824,7 @@ set(GUI_SOURCES ) # keep in sync with the top-level CMake file, only libraries setup is needed here -foreach(x11ext Xshape Xinerama Xrandr Xrender Xfixes Xcursor Xext) +foreach(x11ext Xshape Xinerama Xrandr Xrender Xfixes Xcursor Xext Xpm) if(X11_${x11ext}_FOUND AND X11_${x11ext}_LIB) set(EXTRA_GUI_LIBS ${EXTRA_GUI_LIBS} diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index a6d5876f1..c4ec0f0a3 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -1,7 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2016 Ivailo Monev +** Copyright (C) 2021 Ivailo Monev ** ** This file is part of the QtGui module of the Katie Toolkit. ** @@ -23,250 +22,86 @@ #ifndef QT_NO_IMAGEFORMAT_XPM -#include "qcolor_p.h" #include "qimage.h" -#include "qmap.h" -#include "qvariant.h" -#include "qplatformdefs.h" +#include "qt_x11_p.h" #include "qcorecommon_p.h" +#include + QT_BEGIN_NAMESPACE -static quint64 xpmHash(const char *str) +static inline void qt_ximage_to_qimage(XImage *ximage, QImage &image) { - unsigned int hashValue = 0; - while (*str != '\0') { - hashValue <<= 8; - hashValue += (unsigned int)*str; - ++str; - } - return hashValue; + bool freedata = false; + QX11Data::copyXImageToQImage(ximage, image, &freedata); + QX11Data::destroyXImage(ximage, freedata); } -// Skip until ", read until the next ", return the rest in *buf -// Returns false on error, true on success -static bool read_xpm_string(QByteArray &buf, QIODevice *d, QByteArray &state) +static inline QImage::Format qt_xpm_qimage_format(const XpmAttributes *xpmattributes) { - buf.clear(); - bool gotQuote = false; - int offset = 0; - QSTACKARRAY(char, readbuf, QT_BUFFSIZE); - forever { - if (offset == state.size() || state.isEmpty()) { - qint64 bytesRead = d->read(readbuf, sizeof(readbuf)); - if (bytesRead <= 0) - return false; - state = QByteArray(readbuf, int(bytesRead)); - offset = 0; - } - - if (!gotQuote) { - if (state.at(offset++) == '"') - gotQuote = true; - } else { - char c = state.at(offset++); - if (c == '"') - break; - buf += c; + for (int i = 0; i < xpmattributes->ncolors; i++) { + if (qstricmp(xpmattributes->colorTable[i].c_color, "None") == 0) { + return QImage::Format_ARGB32_Premultiplied; } } - state.remove(0, offset); - return true; + return QImage::systemFormat(); } -// Tests if the given prefix can be the start of an XPM color specification -static bool is_xpm_color_spec_prefix(const QByteArray& prefix) +static inline QImage qt_xpm_to_qimage(XImage *ximage, XImage *ximagemask, const QImage::Format format) { - return prefix == "c" || - prefix == "g" || - prefix == "g4" || - prefix == "m" || - prefix == "s"; -} - -// Reads XPM header. -static bool read_xpm_header( - QIODevice *device, QByteArray &state, - int *cpp, int *ncols, int *w, int *h) -{ - QByteArray buf(200, 0); - - if (!read_xpm_string(buf, device, state)) - return false; - - if (sscanf(buf, "%d %d %d %d", w, h, ncols, cpp) < 4) - return false; // < 4 numbers parsed - - return true; -} - -// Reads XPM body (color information & pixels). -static bool read_xpm_body( - QIODevice *device, QByteArray& state, - int cpp, int ncols, int w, int h, QImage& image) -{ - if (cpp < 0 || cpp > 15) - return false; - - QMap colorMap; - bool hasTransparency = false; - QByteArray buf(200, 0); - - for(int currentColor=0; currentColor < ncols; ++currentColor) { - if (Q_UNLIKELY(!read_xpm_string(buf, device, state))) { - qWarning("QImage: XPM color specification missing"); - return false; - } - QByteArray index = buf.left(cpp); - buf = buf.mid(cpp).simplified().trimmed().toLower(); - QList tokens = buf.split(' '); - int i = tokens.indexOf("c"); - if (i < 0) - i = tokens.indexOf("g"); - if (i < 0) - i = tokens.indexOf("g4"); - if (i < 0) - i = tokens.indexOf("m"); - if (Q_UNLIKELY(i < 0)) { - qWarning("QImage: XPM color specification is missing: %s", buf.constData()); - return false; // no c/g/g4/m specification at all - } - QByteArray color; - while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) { - color.append(tokens.at(i)); - } - if (Q_UNLIKELY(color.isEmpty())) { - qWarning("QImage: XPM color value is missing from specification: %s", buf.constData()); - return false; // no color value - } - buf = color; - if (buf == "none") { - hasTransparency = true; - colorMap.insert(xpmHash(index.constData()), 0); - } else { - QRgb c_rgb = 0; - if (buf[0] == '#') { - if ((buf.length()-1) % 3) { - buf.truncate(((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick - } - qt_get_hex_rgb(buf, buf.length(), &c_rgb); - } else { - for (qint16 j = 0; j < X11RGBTblSize; j++) { - if (qstrcmp(X11RGBTbl[j].name, buf) == 0) { - c_rgb = X11RGBTbl[j].value; - break; - } - } - } - colorMap.insert(xpmHash(index.constData()), 0xff000000 | c_rgb); - } + QImage qimage(ximage->width, ximage->height, format); + qt_ximage_to_qimage(ximage, qimage); + if (ximagemask) { + QImage qimagemask(ximagemask->width, ximagemask->height, QImage::Format_Mono); + qt_ximage_to_qimage(ximagemask, qimagemask); + qimage.setAlphaChannel(qimagemask); } - - // Now we can create 32-bit image of appropriate format - image = QImage(w, h, hasTransparency ? QImage::Format_ARGB32 : QImage::Format_RGB32); - if (Q_UNLIKELY(image.isNull())) { - return false; - } - - // Read pixels - QSTACKARRAY(char, b, 16); - for(int y=0; y= 0; --i) - device->ungetChar(state[i]); - char c; - while (device->getChar(&c) && c != ';') {} - while (device->getChar(&c) && c != '\n') {} - - return true; + return qimage; } QXpmHandler::QXpmHandler() - : state(Ready) { } -bool QXpmHandler::readHeader() -{ - state = Error; - if (!read_xpm_header(device(), buffer, &cpp, &ncols, &width, &height)) - return false; - state = ReadHeader; - return true; -} - bool QXpmHandler::canRead() const { - if (state == Ready && !canRead(device())) - return false; - - if (state != Error) { + if (QXpmHandler::canRead(device())) { setFormat("xpm"); return true; } - return false; } bool QXpmHandler::read(QImage *image) { - if (!canRead()) - return false; - - if (state == Error) - return false; - - if (state == Ready && !readHeader()) { - state = Error; + if (!canRead()) { return false; } - if (!read_xpm_body(device(), buffer, cpp, ncols, width, height, *image)) { - state = Error; + QByteArray data = device()->readAll(); + XImage *ximage = nullptr; + XImage *ximagemask = nullptr; + XpmAttributes xpmattributes; + ::memset(&xpmattributes, 0, sizeof(XpmAttributes)); + const int xpmresult = XpmCreateImageFromBuffer( + qt_x11Data->display, + data.data(), &ximage, &ximagemask, &xpmattributes + ); + if (xpmresult != XpmSuccess) { + qWarning("QXpmHandler::read: %s", XpmGetErrorString(xpmresult)); + XpmFreeAttributes(&xpmattributes); + return false; + } else if (!ximage) { + qWarning("QXpmHandler::read: null XImage"); + XpmFreeAttributes(&xpmattributes); return false; } - - state = Ready; + const QImage::Format format = qt_xpm_qimage_format(&xpmattributes); + *image = qt_xpm_to_qimage(ximage, ximagemask, format); + XpmFreeAttributes(&xpmattributes); return true; } -bool QXpmHandler::supportsOption(QImageIOHandler::ImageOption option) const -{ - return (option == QImageIOHandler::Size); -} - -QVariant QXpmHandler::option(QImageIOHandler::ImageOption option) const -{ - if (option == QImageIOHandler::Size) { - if (state == Error) - return QVariant(); - if (state == Ready && !const_cast(this)->readHeader()) - return QVariant(); - return QSize(width, height); - } - return QVariant(); -} - QByteArray QXpmHandler::name() const { return "xpm"; @@ -283,7 +118,7 @@ bool QXpmHandler::canRead(QIODevice *device) if (device->peek(head, sizeof(head)) != sizeof(head)) return false; - return (::memcmp(head, "/* XPM", 6) == 0); + return (qstrncmp(head, "/* XPM", 6) == 0 || qstrncmp(head, "! XPM2", 6) == 0); } QT_END_NAMESPACE diff --git a/src/gui/image/qxpmhandler_p.h b/src/gui/image/qxpmhandler_p.h index 7884c37ca..bed1ada87 100644 --- a/src/gui/image/qxpmhandler_p.h +++ b/src/gui/image/qxpmhandler_p.h @@ -46,26 +46,9 @@ public: bool canRead() const final; bool read(QImage *image) final; - bool supportsOption(QImageIOHandler::ImageOption option) const final; - QVariant option(QImageIOHandler::ImageOption option) const final; - QByteArray name() const final; static bool canRead(QIODevice *device); - -private: - bool readHeader(); - enum State { - Ready, - ReadHeader, - Error - }; - State state; - int width; - int height; - int ncols; - int cpp; - QByteArray buffer; }; QT_END_NAMESPACE