833 QIODevice *device,
const char *
const * source,
int& index, QByteArray& state,
834 int cpp,
int ncols,
int w,
int h, QImage& image)
836 QByteArray buf(200, 0);
839 if (cpp < 0 || cpp > 15)
847 if (!QImageIOHandler::allocateImage(QSize(w, h), QImage::Format_Indexed8, &image))
849 image.setColorCount(ncols);
852 QMap<quint64,
int> colorMap;
854 bool hasTransparency =
false;
856 for(currentColor=0; currentColor < ncols; ++currentColor) {
857 if (!read_xpm_string(buf, device, source, index, state)) {
858 qCWarning(lcImageIo,
"XPM color specification missing");
862 index = buf.left(cpp);
863 buf = buf.mid(cpp).simplified().trimmed().toLower();
864 QList<QByteArray> tokens = buf.split(
' ');
865 i = tokens.indexOf(
'c');
867 i = tokens.indexOf(
'g');
869 i = tokens.indexOf(
"g4");
871 i = tokens.indexOf(
'm');
873 qCWarning(lcImageIo,
"XPM color specification is missing: %s", buf.constData());
877 while ((++i < tokens.size()) && !is_xpm_color_spec_prefix(tokens.at(i))) {
878 color.append(tokens.at(i));
880 if (color.isEmpty()) {
881 qCWarning(lcImageIo,
"XPM color value is missing from specification: %s", buf.constData());
886 hasTransparency =
true;
887 int transparentColor = currentColor;
889 image.setColor(transparentColor, 0);
890 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), transparentColor);
892 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0);
896 if (((buf.size()-1) % 3) && (buf[0] ==
'#')) {
897 buf.truncate(((buf.size()-1) / 4 * 3) + 1);
900 c_rgb = qt_get_hex_rgb(buf).value_or(0);
902 c_rgb = qt_get_named_xpm_rgb(buf).value_or(0);
905 image.setColor(currentColor, 0xff000000 | c_rgb);
906 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), currentColor);
908 colorMap.insert(xpmHash(QLatin1StringView(index.constData())), 0xff000000 | c_rgb);
915 QImage::Format format = hasTransparency ?
916 QImage::Format_ARGB32 : QImage::Format_RGB32;
917 if (!QImageIOHandler::allocateImage(QSize(w, h), format, &image))
922 for(
int y=0; y<h; y++) {
923 if (!read_xpm_string(buf, device, source, index, state)) {
924 qCWarning(lcImageIo,
"XPM pixels missing on image line %d", y);
927 if (image.depth() == 8) {
928 uchar *p = image.scanLine(y);
929 uchar *d = (uchar *)buf.data();
930 uchar *end = d + buf.size();
935 for (x=0; x<w && d<end; x++) {
937 *p++ = (uchar)colorMap[xpmHash(b)];
942 for (x = 0; x < w && d + cpp <= end; x++) {
943 memcpy(b, (
char *)d, cpp);
944 *p++ = (uchar)colorMap[xpmHash(b)];
950 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
954 QRgb *p = (QRgb*)image.scanLine(y);
955 uchar *d = (uchar *)buf.data();
956 uchar *end = d + buf.size();
960 for (x = 0; x < w && d + cpp <= end; x++) {
961 memcpy(b, (
char *)d, cpp);
962 *p++ = (QRgb)colorMap[xpmHash(b)];
967 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
968 memset(p, 0, (w - x)*4);
975 for (
int i = state.size() - 1; i >= 0; --i)
976 device->ungetChar(state[i]);
978 while (device->getChar(&c) && c !=
';') {}
979 while (device->getChar(&c) && c !=
'\n') {}
996 QByteArray buf(200, 0);
999 int cpp, ncols, w, h, index = 0;
1004 if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
1007 static constexpr auto matcher = qMakeStaticByteArrayMatcher(
"/* XPM");
1009 if (matcher.indexIn(buf) != 0) {
1010 while (readBytes > 0) {
1011 device->ungetChar(buf.at(readBytes - 1));
1018 if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
1021 return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
1065static bool write_xpm_image(
const QImage &sourceImage, QIODevice *device,
const QString &fileName)
1067 if (!device->isWritable())
1071 if (sourceImage.format() != QImage::Format_RGB32 && sourceImage.format() != QImage::Format_ARGB32 && sourceImage.format() != QImage::Format_ARGB32_Premultiplied)
1072 image = sourceImage.convertToFormat(QImage::Format_RGB32);
1074 image = sourceImage;
1076#ifdef __cpp_lib_memory_resource
1078 std::pmr::monotonic_buffer_resource res{&buffer,
sizeof buffer};
1079 std::pmr::map<QRgb,
int> colorMap(&res);
1081 std::map<QRgb,
int> colorMap;
1084 const int w = image.width();
1085 const int h = image.height();
1089 for (
int y = 0; y < h; ++y) {
1090 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1091 for (
int x = 0; x < w; ++x) {
1092 const auto [it, inserted] = colorMap.try_emplace(yp[x], ncolors);
1100 for (
int k = 64; ncolors > k; k *= 64) {
1105 qCWarning(lcImageIo,
"Qt does not support writing XPM images with more than "
1106 "64^4 colors (requested: %d colors).", ncolors);
1113 s <<
"/* XPM */" << Qt::endl
1114 <<
"static char *" << fbname(fileName) <<
"[]={" << Qt::endl
1115 <<
'\"' << w <<
' ' << h <<
' ' << ncolors <<
' ' << cpp <<
'\"';
1118 for (
const auto &[color, index] : colorMap) {
1119 const QString line = image.format() != QImage::Format_RGB32 && !qAlpha(color)
1120 ? QString::asprintf(
"\"%s c None\"", xpm_color_name(cpp, index))
1121 : QString::asprintf(
"\"%s c #%02x%02x%02x\"", xpm_color_name(cpp, index),
1122 qRed(color), qGreen(color), qBlue(color));
1123 s <<
',' << Qt::endl << line;
1127 for (
int y = 0; y < h; ++y) {
1128 s <<
',' << Qt::endl <<
'\"';
1129 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1130 for (
int x = 0; x < w; ++x)
1131 s << xpm_color_name(cpp, colorMap[yp[x]]);
1134 s <<
"};" << Qt::endl;
1135 return static_cast<
bool>(s);
1146 if (!read_xpm_header(device(),
nullptr, index, buffer, &cpp, &ncols, &width, &height))
1162 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)