834 QIODevice *device,
const char *
const * source,
int& index, QByteArray& state,
835 int cpp,
int ncols,
int w,
int h, QImage& image)
837 QByteArray buf(200, 0);
840 if (cpp < 0 || cpp > 15)
845 const QImage::Format format = ncols <= 256 ? QImage::Format_Indexed8 : QImage::Format_ARGB32;
846 if (!QImageIOHandler::allocateImage(QSize(w, h), format, &image))
849 image.setColorCount(ncols);
850 }
else if (
const int mbLimit = QImageReader::allocationLimit()) {
852 if (image.sizeInBytes() + qint64(ncols) * 64 > qint64(mbLimit) * 1024 * 1024) {
853 qCWarning(lcImageIo,
"XPM color table exceeds allocation limit");
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);
919 if (ncols > 256 && !hasTransparency)
920 image.reinterpretAsFormat(QImage::Format_RGB32);
923 for(
int y=0; y<h; y++) {
924 if (!read_xpm_string(buf, device, source, index, state)) {
925 qCWarning(lcImageIo,
"XPM pixels missing on image line %d", y);
928 if (image.depth() == 8) {
929 uchar *p = image.scanLine(y);
930 uchar *d = (uchar *)buf.data();
931 uchar *end = d + buf.size();
936 for (x=0; x<w && d<end; x++) {
938 *p++ = (uchar)colorMap.value(xpmHash(b));
943 for (x = 0; x < w && d + cpp <= end; x++) {
944 memcpy(b, (
char *)d, cpp);
945 *p++ = (uchar)colorMap.value(xpmHash(b));
951 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
955 QRgb *p = (QRgb*)image.scanLine(y);
956 uchar *d = (uchar *)buf.data();
957 uchar *end = d + buf.size();
961 for (x = 0; x < w && d + cpp <= end; x++) {
962 memcpy(b, (
char *)d, cpp);
963 *p++ = (QRgb)colorMap.value(xpmHash(b));
968 qCWarning(lcImageIo,
"XPM pixels missing on image line %d (possibly a C++ trigraph).", y);
969 memset(p, 0, (w - x)*4);
976 for (
int i = state.size() - 1; i >= 0; --i)
977 device->ungetChar(state[i]);
979 while (device->getChar(&c) && c !=
';') {}
980 while (device->getChar(&c) && c !=
'\n') {}
997 QByteArray buf(200, 0);
1000 int cpp, ncols, w, h, index = 0;
1005 if ((readBytes = device->readLine(buf.data(), buf.size())) < 0)
1008 static constexpr auto matcher = qMakeStaticByteArrayMatcher(
"/* XPM");
1010 if (matcher.indexIn(buf) != 0) {
1011 while (readBytes > 0) {
1012 device->ungetChar(buf.at(readBytes - 1));
1019 if (!read_xpm_header(device, source, index, state, &cpp, &ncols, &w, &h))
1022 return read_xpm_body(device, source, index, state, cpp, ncols, w, h, image);
1066static bool write_xpm_image(
const QImage &sourceImage, QIODevice *device,
const QString &fileName)
1068 if (!device->isWritable())
1072 if (sourceImage.format() != QImage::Format_RGB32 && sourceImage.format() != QImage::Format_ARGB32 && sourceImage.format() != QImage::Format_ARGB32_Premultiplied)
1073 image = sourceImage.convertToFormat(QImage::Format_RGB32);
1075 image = sourceImage;
1077#ifdef __cpp_lib_memory_resource
1079 std::pmr::monotonic_buffer_resource res{&buffer,
sizeof buffer};
1080 std::pmr::map<QRgb,
int> colorMap(&res);
1082 std::map<QRgb,
int> colorMap;
1085 const int w = image.width();
1086 const int h = image.height();
1090 for (
int y = 0; y < h; ++y) {
1091 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1092 for (
int x = 0; x < w; ++x) {
1093 const auto [it, inserted] = colorMap.try_emplace(yp[x], ncolors);
1101 for (
int k = 64; ncolors > k; k *= 64) {
1106 qCWarning(lcImageIo,
"Qt does not support writing XPM images with more than "
1107 "64^4 colors (requested: %d colors).", ncolors);
1114 s <<
"/* XPM */" << Qt::endl
1115 <<
"static char *" << fbname(fileName) <<
"[]={" << Qt::endl
1116 <<
'\"' << w <<
' ' << h <<
' ' << ncolors <<
' ' << cpp <<
'\"';
1119 for (
const auto &[color, index] : colorMap) {
1120 const QString line = image.format() != QImage::Format_RGB32 && !qAlpha(color)
1121 ? QString::asprintf(
"\"%s c None\"", xpm_color_name(cpp, index))
1122 : QString::asprintf(
"\"%s c #%02x%02x%02x\"", xpm_color_name(cpp, index),
1123 qRed(color), qGreen(color), qBlue(color));
1124 s <<
',' << Qt::endl << line;
1128 for (
int y = 0; y < h; ++y) {
1129 s <<
',' << Qt::endl <<
'\"';
1130 const QRgb *yp =
reinterpret_cast<
const QRgb *>(image.constScanLine(y));
1131 for (
int x = 0; x < w; ++x)
1132 s << xpm_color_name(cpp, colorMap[yp[x]]);
1135 s <<
"};" << Qt::endl;
1136 return static_cast<
bool>(s);
1147 if (!read_xpm_header(device(),
nullptr, index, buffer, &cpp, &ncols, &width, &height))
1163 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)