839 QIODevice *device,
const char *
const * source,
int& index, QByteArray& state,
840 int cpp,
int ncols,
int w,
int h, QImage& image)
842 QByteArray buf(200, 0);
845 if (cpp < 0 || cpp > 15)
853 if (!QImageIOHandler::allocateImage(QSize(w, h), QImage::Format_Indexed8, &image))
855 image.setColorCount(ncols);
858 QMap<quint64,
int> colorMap;
860 bool hasTransparency =
false;
862 for(currentColor=0; currentColor < ncols; ++currentColor) {
863 if (!read_xpm_string(buf, device, source, index, state)) {
864 qCWarning(lcImageIo,
"XPM color specification missing");
868 index = buf.left(cpp);
869 buf = buf.mid(cpp).simplified().trimmed().toLower();
870 QList<QByteArray> tokens = buf.split(
' ');
871 i = tokens.indexOf(
"c");
873 i = tokens.indexOf(
"g");
875 i = tokens.indexOf(
"g4");
877 i = tokens.indexOf(
"m");
879 qCWarning(lcImageIo,
"XPM color specification is missing: %s", buf.constData());
883 while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) {
884 color.append(tokens.at(i));
886 if (color.isEmpty()) {
887 qCWarning(lcImageIo,
"XPM color value is missing from specification: %s", buf.constData());
892 hasTransparency =
true;
893 int transparentColor = currentColor;
895 image.setColor(transparentColor, 0);
896 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), transparentColor);
898 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0);
902 if (((buf.size()-1) % 3) && (buf[0] ==
'#')) {
903 buf.truncate(((buf.size()-1) / 4 * 3) + 1);
906 c_rgb = qt_get_hex_rgb(buf).value_or(0);
908 c_rgb = qt_get_named_xpm_rgb(buf).value_or(0);
911 image.setColor(currentColor, 0xff000000 | c_rgb);
912 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), currentColor);
914 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0xff000000 | c_rgb);
921 QImage::Format format = hasTransparency ?
922 QImage::Format_ARGB32 : QImage::Format_RGB32;
923 if (!QImageIOHandler::allocateImage(QSize(w, h), format, &image))
928 for(
int y=0; y<h; y++) {
929 if (!read_xpm_string(buf, device, source, index, state)) {
930 qCWarning(lcImageIo,
"XPM pixels missing on image line %d", y);
933 if (image.depth() == 8) {
934 uchar *p = image.scanLine(y);
935 uchar *d = (uchar *)buf.data();
936 uchar *end = d + buf.size();
941 for (x=0; x<w && d<end; x++) {
943 *p++ = (uchar)colorMap[xpmHash(b)];
948 for (x = 0; x < w && d + cpp <= end; x++) {
949 memcpy(b, (
char *)d, cpp);
950 *p++ = (uchar)colorMap[xpmHash(b)];
956 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
960 QRgb *p = (QRgb*)image.scanLine(y);
961 uchar *d = (uchar *)buf.data();
962 uchar *end = d + buf.size();
966 for (x = 0; x < w && d + cpp <= end; x++) {
967 memcpy(b, (
char *)d, cpp);
968 *p++ = (QRgb)colorMap[xpmHash(b)];
973 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
974 memset(p, 0, (w - x)*4);
981 for (
int i = state.size() - 1; i >= 0; --i)
982 device->ungetChar(state[i]);
984 while (device->getChar(&c) && c !=
';') {}
985 while (device->getChar(&c) && c !=
'\n') {}
1002 QByteArray buf(200, 0);
1005 int cpp, ncols, w, h, index = 0;
1010 if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
1013 static constexpr auto matcher = qMakeStaticByteArrayMatcher(
"/* XPM");
1015 if (matcher.indexIn(buf) != 0) {
1016 while (readBytes > 0) {
1017 device->ungetChar(buf.at(readBytes - 1));
1024 if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
1027 return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
1071static bool write_xpm_image(
const QImage &sourceImage, QIODevice *device,
const QString &fileName)
1073 if (!device->isWritable())
1077 if (sourceImage.format() != QImage::Format_RGB32 && sourceImage.format() != QImage::Format_ARGB32 && sourceImage.format() != QImage::Format_ARGB32_Premultiplied)
1078 image = sourceImage.convertToFormat(QImage::Format_RGB32);
1080 image = sourceImage;
1082#ifdef __cpp_lib_memory_resource
1084 std::pmr::monotonic_buffer_resource res{&buffer,
sizeof buffer};
1085 std::pmr::map<QRgb,
int> colorMap(&res);
1087 std::map<QRgb,
int> colorMap;
1090 const int w = image.width();
1091 const int h = image.height();
1095 for (
int y = 0; y < h; ++y) {
1096 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1097 for (
int x = 0; x < w; ++x) {
1098 const auto [it, inserted] = colorMap.try_emplace(yp[x], ncolors);
1106 for (
int k = 64; ncolors > k; k *= 64) {
1111 qCWarning(lcImageIo,
"Qt does not support writing XPM images with more than "
1112 "64^4 colors (requested: %d colors).", ncolors);
1119 s <<
"/* XPM */" << Qt::endl
1120 <<
"static char *" << fbname(fileName) <<
"[]={" << Qt::endl
1121 <<
'\"' << w <<
' ' << h <<
' ' << ncolors <<
' ' << cpp <<
'\"';
1124 for (
const auto &[color, index] : colorMap) {
1125 const QString line = image.format() != QImage::Format_RGB32 && !qAlpha(color)
1126 ? QString::asprintf(
"\"%s c None\"", xpm_color_name(cpp, index))
1127 : QString::asprintf(
"\"%s c #%02x%02x%02x\"", xpm_color_name(cpp, index),
1128 qRed(color), qGreen(color), qBlue(color));
1129 s <<
',' << Qt::endl << line;
1133 for (
int y = 0; y < h; ++y) {
1134 s <<
',' << Qt::endl <<
'\"';
1135 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1136 for (
int x = 0; x < w; ++x)
1137 s << xpm_color_name(cpp, colorMap[yp[x]]);
1140 s <<
"};" << Qt::endl;
1141 return static_cast<
bool>(s);
1152 if (!read_xpm_header(device(),
nullptr, index, buffer, &cpp, &ncols, &width, &height))
1168 if (!read_xpm_body(device(),
nullptr, index, buffer, cpp, ncols, width, height, *image)) {
static bool read_xpm_body(QIODevice *device, const char *const *source, int &index, QByteArray &state, int cpp, int ncols, int w, int h, QImage &image)