838 QIODevice *device,
const char *
const * source,
int& index, QByteArray& state,
839 int cpp,
int ncols,
int w,
int h, QImage& image)
841 QByteArray buf(200, 0);
844 if (cpp < 0 || cpp > 15)
852 if (!QImageIOHandler::allocateImage(QSize(w, h), QImage::Format_Indexed8, &image))
854 image.setColorCount(ncols);
857 QMap<quint64,
int> colorMap;
859 bool hasTransparency =
false;
861 for(currentColor=0; currentColor < ncols; ++currentColor) {
862 if (!read_xpm_string(buf, device, source, index, state)) {
863 qCWarning(lcImageIo,
"XPM color specification missing");
867 index = buf.left(cpp);
868 buf = buf.mid(cpp).simplified().trimmed().toLower();
869 QList<QByteArray> tokens = buf.split(
' ');
870 i = tokens.indexOf(
"c");
872 i = tokens.indexOf(
"g");
874 i = tokens.indexOf(
"g4");
876 i = tokens.indexOf(
"m");
878 qCWarning(lcImageIo,
"XPM color specification is missing: %s", buf.constData());
882 while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) {
883 color.append(tokens.at(i));
885 if (color.isEmpty()) {
886 qCWarning(lcImageIo,
"XPM color value is missing from specification: %s", buf.constData());
891 hasTransparency =
true;
892 int transparentColor = currentColor;
894 image.setColor(transparentColor, 0);
895 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), transparentColor);
897 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0);
901 if (((buf.size()-1) % 3) && (buf[0] ==
'#')) {
902 buf.truncate(((buf.size()-1) / 4 * 3) + 1);
905 c_rgb = qt_get_hex_rgb(buf).value_or(0);
907 c_rgb = qt_get_named_xpm_rgb(buf).value_or(0);
910 image.setColor(currentColor, 0xff000000 | c_rgb);
911 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), currentColor);
913 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0xff000000 | c_rgb);
920 QImage::Format format = hasTransparency ?
921 QImage::Format_ARGB32 : QImage::Format_RGB32;
922 if (!QImageIOHandler::allocateImage(QSize(w, h), format, &image))
927 for(
int y=0; y<h; y++) {
928 if (!read_xpm_string(buf, device, source, index, state)) {
929 qCWarning(lcImageIo,
"XPM pixels missing on image line %d", y);
932 if (image.depth() == 8) {
933 uchar *p = image.scanLine(y);
934 uchar *d = (uchar *)buf.data();
935 uchar *end = d + buf.size();
940 for (x=0; x<w && d<end; x++) {
942 *p++ = (uchar)colorMap[xpmHash(b)];
947 for (x = 0; x < w && d + cpp <= end; x++) {
948 memcpy(b, (
char *)d, cpp);
949 *p++ = (uchar)colorMap[xpmHash(b)];
955 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
959 QRgb *p = (QRgb*)image.scanLine(y);
960 uchar *d = (uchar *)buf.data();
961 uchar *end = d + buf.size();
965 for (x = 0; x < w && d + cpp <= end; x++) {
966 memcpy(b, (
char *)d, cpp);
967 *p++ = (QRgb)colorMap[xpmHash(b)];
972 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
973 memset(p, 0, (w - x)*4);
980 for (
int i = state.size() - 1; i >= 0; --i)
981 device->ungetChar(state[i]);
983 while (device->getChar(&c) && c !=
';') {}
984 while (device->getChar(&c) && c !=
'\n') {}
1001 QByteArray buf(200, 0);
1004 int cpp, ncols, w, h, index = 0;
1009 if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
1012 static constexpr auto matcher = qMakeStaticByteArrayMatcher(
"/* XPM");
1014 if (matcher.indexIn(buf) != 0) {
1015 while (readBytes > 0) {
1016 device->ungetChar(buf.at(readBytes - 1));
1023 if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
1026 return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
1070static bool write_xpm_image(
const QImage &sourceImage, QIODevice *device,
const QString &fileName)
1072 if (!device->isWritable())
1076 if (sourceImage.format() != QImage::Format_RGB32 && sourceImage.format() != QImage::Format_ARGB32 && sourceImage.format() != QImage::Format_ARGB32_Premultiplied)
1077 image = sourceImage.convertToFormat(QImage::Format_RGB32);
1079 image = sourceImage;
1081#ifdef __cpp_lib_memory_resource
1083 std::pmr::monotonic_buffer_resource res{&buffer,
sizeof buffer};
1084 std::pmr::map<QRgb,
int> colorMap(&res);
1086 std::map<QRgb,
int> colorMap;
1089 const int w = image.width();
1090 const int h = image.height();
1094 for (
int y = 0; y < h; ++y) {
1095 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1096 for (
int x = 0; x < w; ++x) {
1097 const auto [it, inserted] = colorMap.try_emplace(yp[x], ncolors);
1105 for (
int k = 64; ncolors > k; k *= 64) {
1110 qCWarning(lcImageIo,
"Qt does not support writing XPM images with more than "
1111 "64^4 colors (requested: %d colors).", ncolors);
1118 s <<
"/* XPM */" << Qt::endl
1119 <<
"static char *" << fbname(fileName) <<
"[]={" << Qt::endl
1120 <<
'\"' << w <<
' ' << h <<
' ' << ncolors <<
' ' << cpp <<
'\"';
1123 for (
const auto &[color, index] : colorMap) {
1124 const QString line = image.format() != QImage::Format_RGB32 && !qAlpha(color)
1125 ? QString::asprintf(
"\"%s c None\"", xpm_color_name(cpp, index))
1126 : QString::asprintf(
"\"%s c #%02x%02x%02x\"", xpm_color_name(cpp, index),
1127 qRed(color), qGreen(color), qBlue(color));
1128 s <<
',' << Qt::endl << line;
1132 for (
int y = 0; y < h; ++y) {
1133 s <<
',' << Qt::endl <<
'\"';
1134 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1135 for (
int x = 0; x < w; ++x)
1136 s << xpm_color_name(cpp, colorMap[yp[x]]);
1139 s <<
"};" << Qt::endl;
1140 return static_cast<
bool>(s);
1151 if (!read_xpm_header(device(),
nullptr, index, buffer, &cpp, &ncols, &width, &height))
1167 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)