File indexing completed on 2025-03-09 04:54:33

0001 /*
0002   This file is part of libkdepim.
0003 
0004   Original compface:
0005   Copyright (c) James Ashton - Sydney University - June 1990. //krazy:exclude=copyright
0006 
0007   Additions for KDE:
0008   SPDX-FileCopyrightText: 2004 Jakob Schröter <js@camaya.net>
0009 
0010   SPDX-License-Identifier: LGPL-2.0-or-later
0011 */
0012 
0013 #include "kxface.h"
0014 
0015 #include <QBuffer>
0016 #include <QImage>
0017 #include <QRegularExpression>
0018 #include <QString>
0019 
0020 #include <cstdlib>
0021 #include <cstring>
0022 
0023 #define GEN(g)                                                                                                                                                 \
0024     F[h] ^= G.g[k];                                                                                                                                            \
0025     break
0026 
0027 #define BITSPERDIG 4
0028 #define DIGITS (PIXELS / BITSPERDIG)
0029 #define DIGSPERWORD 4
0030 #define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG)
0031 
0032 /* compressed output uses the full range of printable characters.
0033  * in ascii these are in a contiguous block so we just need to know
0034  * the first and last.  The total number of printables is needed too */
0035 #define FIRSTPRINT '!'
0036 #define LASTPRINT '~'
0037 #define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1)
0038 
0039 /* output line length for compressed data */
0040 static const int MAXLINELEN = 78;
0041 
0042 /* Portable, very large unsigned integer arithmetic is needed.
0043  * Implementation uses arrays of WORDs.  COMPs must have at least
0044  * twice as many bits as WORDs to handle intermediate results */
0045 #define COMP unsigned long
0046 #define WORDCARRY (1 << BITSPERWORD)
0047 #define WORDMASK (WORDCARRY - 1)
0048 
0049 #define ERR_OK 0 /* successful completion */
0050 #define ERR_EXCESS 1 /* completed OK but some input was ignored */
0051 #define ERR_INSUFF -1 /* insufficient input.  Bad face format? */
0052 #define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */
0053 
0054 #define BLACK 0
0055 #define GREY 1
0056 #define WHITE 2
0057 
0058 static const int MAX_XFACE_LENGTH = 2048;
0059 
0060 using namespace MessageViewer;
0061 
0062 KXFace::KXFace()
0063 {
0064     NumProbs = 0;
0065 }
0066 
0067 KXFace::~KXFace() = default;
0068 
0069 QString KXFace::fromImage(const QImage &image)
0070 {
0071     if (image.isNull()) {
0072         return {};
0073     }
0074 
0075     QImage scaledImg = image.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
0076     QByteArray ba;
0077     QBuffer buffer(&ba, this);
0078     buffer.open(QIODevice::WriteOnly);
0079     scaledImg.save(&buffer, "XBM");
0080     QString xbm(QString::fromLatin1(ba));
0081     xbm.remove(0, xbm.indexOf(QLatin1StringView("{")) + 1);
0082     xbm.truncate(xbm.indexOf(QLatin1StringView("}")));
0083     xbm.remove(QLatin1Char(' '));
0084     xbm.remove(QLatin1Char(','));
0085     xbm.remove(QStringLiteral("0x"));
0086     xbm.remove(QLatin1Char('\n'));
0087     xbm.truncate(576);
0088     QString tmp = QLatin1StringView(xbm.toLatin1());
0089     int len = tmp.length();
0090     for (int i = 0; i < len; ++i) {
0091         switch (tmp[i].toLatin1()) {
0092         case '1':
0093             tmp[i] = QLatin1Char('8');
0094             break;
0095         case '2':
0096             tmp[i] = QLatin1Char('4');
0097             break;
0098         case '3':
0099             tmp[i] = QLatin1Char('c');
0100             break;
0101         case '4':
0102             tmp[i] = QLatin1Char('2');
0103             break;
0104         case '5':
0105             tmp[i] = QLatin1Char('a');
0106             break;
0107         case '7':
0108             tmp[i] = QLatin1Char('e');
0109             break;
0110         case '8':
0111             tmp[i] = QLatin1Char('1');
0112             break;
0113         case 'A':
0114         case 'a':
0115             tmp[i] = QLatin1Char('5');
0116             break;
0117         case 'B':
0118         case 'b':
0119             tmp[i] = QLatin1Char('d');
0120             break;
0121         case 'C':
0122         case 'c':
0123             tmp[i] = QLatin1Char('3');
0124             break;
0125         case 'D':
0126         case 'd':
0127             tmp[i] = QLatin1Char('b');
0128             break;
0129         case 'E':
0130         case 'e':
0131             tmp[i] = QLatin1Char('7');
0132             break;
0133         }
0134         if (i % 2) {
0135             QChar t = tmp[i];
0136             tmp[i] = tmp[i - 1];
0137             tmp[i - 1] = t;
0138         }
0139     }
0140     tmp.replace(QRegularExpression(QStringLiteral("(\\w{12})")), QStringLiteral("\\1\n"));
0141     tmp.replace(QRegularExpression(QStringLiteral("(\\w{4})")), QStringLiteral("0x\\1,"));
0142     len = tmp.length();
0143     char *fbuf = (char *)malloc(len + 1);
0144     strncpy(fbuf, tmp.toLatin1().constData(), len);
0145     fbuf[len] = '\0';
0146     if (!(status = setjmp(comp_env))) {
0147         ReadFace(fbuf);
0148         GenFace();
0149         CompAll(fbuf);
0150     }
0151     QString ret(QString::fromLatin1(fbuf));
0152     free(fbuf);
0153 
0154     return ret;
0155 }
0156 
0157 QImage KXFace::toImage(const QString &xface)
0158 {
0159     if (xface.length() > MAX_XFACE_LENGTH) {
0160         return {};
0161     }
0162 
0163     char *fbuf = (char *)malloc(MAX_XFACE_LENGTH);
0164     memset(fbuf, '\0', MAX_XFACE_LENGTH);
0165     strncpy(fbuf, xface.toLatin1().constData(), xface.length());
0166     QByteArray img;
0167     if (!(status = setjmp(comp_env))) {
0168         UnCompAll(fbuf); /* compress otherwise */
0169         UnGenFace();
0170         img = WriteFace();
0171     }
0172     free(fbuf);
0173     QImage p;
0174     p.loadFromData(img, "XBM");
0175 
0176     return p;
0177 }
0178 
0179 //============================================================================
0180 // more or less original compface 1.4 source
0181 
0182 void KXFace::RevPush(const Prob *p)
0183 {
0184     if (NumProbs >= PIXELS * 2 - 1) {
0185         longjmp(comp_env, ERR_INTERNAL);
0186     }
0187     ProbBuf[NumProbs++] = (Prob *)p;
0188 }
0189 
0190 void KXFace::BigPush(Prob *p)
0191 {
0192     static unsigned char tmp;
0193 
0194     BigDiv(p->p_range, &tmp);
0195     BigMul(0);
0196     BigAdd(tmp + p->p_offset);
0197 }
0198 
0199 int KXFace::BigPop(const Prob *p)
0200 {
0201     static unsigned char tmp;
0202     int i;
0203 
0204     BigDiv(0, &tmp);
0205     i = 0;
0206     while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) {
0207         p++;
0208         ++i;
0209     }
0210     BigMul(p->p_range);
0211     BigAdd(tmp - p->p_offset);
0212     return i;
0213 }
0214 
0215 /* Divide B by a storing the result in B and the remainder in the word
0216  * pointer to by r
0217  */
0218 void KXFace::BigDiv(unsigned char a, unsigned char *r)
0219 {
0220     int i;
0221     unsigned char *w;
0222     COMP c, d;
0223 
0224     a &= WORDMASK;
0225     if ((a == 1) || (B.b_words == 0)) {
0226         *r = 0;
0227         return;
0228     }
0229     if (a == 0) { /* treat this as a == WORDCARRY */
0230         /* and just shift everything right a WORD (unsigned char)*/
0231         i = --B.b_words;
0232         w = B.b_word;
0233         *r = *w;
0234         while (i--) {
0235             *w = *(w + 1);
0236             w++;
0237         }
0238         *w = 0;
0239         return;
0240     }
0241     w = B.b_word + (i = B.b_words);
0242     c = 0;
0243     while (i--) {
0244         c <<= BITSPERWORD;
0245         c += (COMP) * --w;
0246         d = c / (COMP)a;
0247         c = c % (COMP)a;
0248         *w = (unsigned char)(d & WORDMASK);
0249     }
0250     *r = c;
0251     if (B.b_word[B.b_words - 1] == 0) {
0252         B.b_words--;
0253     }
0254 }
0255 
0256 /* Multiply a by B storing the result in B
0257  */
0258 void KXFace::BigMul(unsigned char a)
0259 {
0260     int i;
0261     unsigned char *w;
0262     COMP c;
0263 
0264     a &= WORDMASK;
0265     if ((a == 1) || (B.b_words == 0)) {
0266         return;
0267     }
0268     if (a == 0) { /* treat this as a == WORDCARRY */
0269         /* and just shift everything left a WORD (unsigned char) */
0270         if ((i = B.b_words++) >= MAXWORDS - 1) {
0271             longjmp(comp_env, ERR_INTERNAL);
0272         }
0273         w = B.b_word + i;
0274         while (i--) {
0275             *w = *(w - 1);
0276             w--;
0277         }
0278         *w = 0;
0279         return;
0280     }
0281     i = B.b_words;
0282     w = B.b_word;
0283     c = 0;
0284     while (i--) {
0285         c += (COMP)*w * (COMP)a;
0286         *(w++) = (unsigned char)(c & WORDMASK);
0287         c >>= BITSPERWORD;
0288     }
0289     if (c) {
0290         if (B.b_words++ >= MAXWORDS) {
0291             longjmp(comp_env, ERR_INTERNAL);
0292         }
0293         *w = (COMP)(c & WORDMASK);
0294     }
0295 }
0296 
0297 /* Add to a to B storing the result in B
0298  */
0299 void KXFace::BigAdd(unsigned char a)
0300 {
0301     int i;
0302     unsigned char *w;
0303     COMP c;
0304 
0305     a &= WORDMASK;
0306     if (a == 0) {
0307         return;
0308     }
0309     i = 0;
0310     w = B.b_word;
0311     c = a;
0312     while ((i < B.b_words) && c) {
0313         c += (COMP)*w;
0314         *w++ = (unsigned char)(c & WORDMASK);
0315         c >>= BITSPERWORD;
0316         ++i;
0317     }
0318     if ((i == B.b_words) && c) {
0319         if (B.b_words++ >= MAXWORDS) {
0320             longjmp(comp_env, ERR_INTERNAL);
0321         }
0322         *w = (COMP)(c & WORDMASK);
0323     }
0324 }
0325 
0326 void KXFace::BigClear()
0327 {
0328     B.b_words = 0;
0329 }
0330 
0331 QByteArray KXFace::WriteFace()
0332 {
0333     char *s;
0334     int i;
0335     int j;
0336     int bits;
0337     int digits;
0338     int words;
0339     // int digsperword = DIGSPERWORD;
0340     // int wordsperline = WORDSPERLINE;
0341     QByteArray t("#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n ");
0342     j = t.length() - 1;
0343 
0344     s = F;
0345     bits = digits = words = i = 0;
0346     t.resize(MAX_XFACE_LENGTH);
0347     int digsperword = 2;
0348     int wordsperline = 15;
0349     while (s < F + PIXELS) {
0350         if ((bits == 0) && (digits == 0)) {
0351             t[j++] = '0';
0352             t[j++] = 'x';
0353         }
0354         if (*(s++)) {
0355             i = (i >> 1) | 0x8;
0356         } else {
0357             i >>= 1;
0358         }
0359         if (++bits == BITSPERDIG) {
0360             j++;
0361             t[j - ((digits & 1) * 2)] = *(i + HexDigits);
0362             bits = i = 0;
0363             if (++digits == digsperword) {
0364                 if (s >= F + PIXELS) {
0365                     break;
0366                 }
0367                 t[j++] = ',';
0368                 digits = 0;
0369                 if (++words == wordsperline) {
0370                     t[j++] = '\n';
0371                     t[j++] = ' ';
0372                     words = 0;
0373                 }
0374             }
0375         }
0376     }
0377     t.resize(j + 1);
0378     t += "};\n";
0379     return t;
0380 }
0381 
0382 void KXFace::UnCompAll(char *fbuf)
0383 {
0384     char *p;
0385 
0386     BigClear();
0387     BigRead(fbuf);
0388     p = F;
0389     while (p < F + PIXELS) {
0390         *(p++) = 0;
0391     }
0392     UnCompress(F, 16, 16, 0);
0393     UnCompress(F + 16, 16, 16, 0);
0394     UnCompress(F + 32, 16, 16, 0);
0395     UnCompress(F + WIDTH * 16, 16, 16, 0);
0396     UnCompress(F + WIDTH * 16 + 16, 16, 16, 0);
0397     UnCompress(F + WIDTH * 16 + 32, 16, 16, 0);
0398     UnCompress(F + WIDTH * 32, 16, 16, 0);
0399     UnCompress(F + WIDTH * 32 + 16, 16, 16, 0);
0400     UnCompress(F + WIDTH * 32 + 32, 16, 16, 0);
0401 }
0402 
0403 void KXFace::UnCompress(char *f, int wid, int hei, int lev)
0404 {
0405     switch (BigPop(&levels[lev][0])) {
0406     case WHITE:
0407         return;
0408     case BLACK:
0409         PopGreys(f, wid, hei);
0410         return;
0411     default:
0412         wid /= 2;
0413         hei /= 2;
0414         lev++;
0415         UnCompress(f, wid, hei, lev);
0416         UnCompress(f + wid, wid, hei, lev);
0417         UnCompress(f + hei * WIDTH, wid, hei, lev);
0418         UnCompress(f + wid + hei * WIDTH, wid, hei, lev);
0419         return;
0420     }
0421 }
0422 
0423 void KXFace::BigWrite(char *fbuf)
0424 {
0425     static unsigned char tmp;
0426     static char buf[DIGITS];
0427     char *s;
0428     int i;
0429 
0430     s = buf;
0431     while (B.b_words > 0) {
0432         BigDiv(NUMPRINTS, &tmp);
0433         *(s++) = tmp + FIRSTPRINT;
0434     }
0435     i = 7; // leave room for the field name on the first line
0436     *(fbuf++) = ' ';
0437     while (s-- > buf) {
0438         if (i == 0) {
0439             *(fbuf++) = ' ';
0440         }
0441         *(fbuf++) = *s;
0442         if (++i >= MAXLINELEN) {
0443             *(fbuf++) = '\n';
0444             i = 0;
0445         }
0446     }
0447     if (i > 0) {
0448         *(fbuf++) = '\n';
0449     }
0450     *(fbuf++) = '\0';
0451 }
0452 
0453 void KXFace::BigRead(char *fbuf)
0454 {
0455     int c;
0456 
0457     while (*fbuf != '\0') {
0458         c = *(fbuf++);
0459         if ((c < FIRSTPRINT) || (c > LASTPRINT)) {
0460             continue;
0461         }
0462         BigMul(NUMPRINTS);
0463         BigAdd((unsigned char)(c - FIRSTPRINT));
0464     }
0465 }
0466 
0467 void KXFace::ReadFace(char *fbuf)
0468 {
0469     int c;
0470     int i;
0471     char *s;
0472     char *t;
0473 
0474     t = s = fbuf;
0475     for (i = strlen(s); i > 0; --i) {
0476         c = (int)*(s++);
0477         if ((c >= '0') && (c <= '9')) {
0478             if (t >= fbuf + DIGITS) {
0479                 status = ERR_EXCESS;
0480                 break;
0481             }
0482             *(t++) = c - '0';
0483         } else if ((c >= 'A') && (c <= 'F')) {
0484             if (t >= fbuf + DIGITS) {
0485                 status = ERR_EXCESS;
0486                 break;
0487             }
0488             *(t++) = c - 'A' + 10;
0489         } else if ((c >= 'a') && (c <= 'f')) {
0490             if (t >= fbuf + DIGITS) {
0491                 status = ERR_EXCESS;
0492                 break;
0493             }
0494             *(t++) = c - 'a' + 10;
0495         } else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t - 1) == 0)) {
0496             t--;
0497         }
0498     }
0499     if (t < fbuf + DIGITS) {
0500         longjmp(comp_env, ERR_INSUFF);
0501     }
0502     s = fbuf;
0503     t = F;
0504     c = 1 << (BITSPERDIG - 1);
0505     while (t < F + PIXELS) {
0506         *(t++) = (*s & c) ? 1 : 0;
0507         if ((c >>= 1) == 0) {
0508             s++;
0509             c = 1 << (BITSPERDIG - 1);
0510         }
0511     }
0512 }
0513 
0514 void KXFace::GenFace()
0515 {
0516     static char newp[PIXELS];
0517     char *f1;
0518     char *f2;
0519     int i;
0520 
0521     f1 = newp;
0522     f2 = F;
0523     i = PIXELS;
0524     while (i-- > 0) {
0525         *(f1++) = *(f2++);
0526     }
0527     Gen(newp);
0528 }
0529 
0530 void KXFace::UnGenFace()
0531 {
0532     Gen(F);
0533 }
0534 
0535 // static
0536 void KXFace::Gen(char *f)
0537 {
0538     int m;
0539     int l;
0540     int k;
0541     int j;
0542     int i;
0543     int h;
0544 
0545     for (j = 0; j < HEIGHT; ++j) {
0546         for (i = 0; i < WIDTH; ++i) {
0547             h = i + j * WIDTH;
0548             k = 0;
0549             for (l = i - 2; l <= i + 2; ++l) {
0550                 for (m = j - 2; m <= j; ++m) {
0551                     if ((l >= i) && (m == j)) {
0552                         continue;
0553                     }
0554                     if ((l > 0) && (l <= WIDTH) && (m > 0)) {
0555                         k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2;
0556                     }
0557                 }
0558             }
0559             switch (i) {
0560             case 1:
0561                 switch (j) {
0562                 case 1:
0563                     GEN(g_22);
0564                 case 2:
0565                     GEN(g_21);
0566                 default:
0567                     GEN(g_20);
0568                 }
0569                 break;
0570             case 2:
0571                 switch (j) {
0572                 case 1:
0573                     GEN(g_12);
0574                 case 2:
0575                     GEN(g_11);
0576                 default:
0577                     GEN(g_10);
0578                 }
0579                 break;
0580             case WIDTH - 1:
0581                 switch (j) {
0582                 case 1:
0583                     GEN(g_42);
0584                 case 2:
0585                     GEN(g_41);
0586                 default:
0587                     GEN(g_40);
0588                 }
0589                 break;
0590             /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in
0591             because it appears exactly like this in the original compface code.
0592             case WIDTH :
0593             switch (j)
0594             {
0595             case 1 : GEN(g_32);
0596             case 2 : GEN(g_31);
0597             default : GEN(g_30);
0598             }
0599             break;
0600             */
0601             default:
0602                 switch (j) {
0603                 case 1:
0604                     GEN(g_02);
0605                 case 2:
0606                     GEN(g_01);
0607                 default:
0608                     GEN(g_00);
0609                 }
0610                 break;
0611             }
0612         }
0613     }
0614 }
0615 
0616 void KXFace::PopGreys(char *f, int wid, int hei)
0617 {
0618     if (wid > 3) {
0619         wid /= 2;
0620         hei /= 2;
0621         PopGreys(f, wid, hei);
0622         PopGreys(f + wid, wid, hei);
0623         PopGreys(f + WIDTH * hei, wid, hei);
0624         PopGreys(f + WIDTH * hei + wid, wid, hei);
0625     } else {
0626         wid = BigPop(freqs);
0627         if (wid & 1) {
0628             *f = 1;
0629         }
0630         if (wid & 2) {
0631             *(f + 1) = 1;
0632         }
0633         if (wid & 4) {
0634             *(f + WIDTH) = 1;
0635         }
0636         if (wid & 8) {
0637             *(f + WIDTH + 1) = 1;
0638         }
0639     }
0640 }
0641 
0642 void KXFace::CompAll(char *fbuf)
0643 {
0644     Compress(F, 16, 16, 0);
0645     Compress(F + 16, 16, 16, 0);
0646     Compress(F + 32, 16, 16, 0);
0647     Compress(F + WIDTH * 16, 16, 16, 0);
0648     Compress(F + WIDTH * 16 + 16, 16, 16, 0);
0649     Compress(F + WIDTH * 16 + 32, 16, 16, 0);
0650     Compress(F + WIDTH * 32, 16, 16, 0);
0651     Compress(F + WIDTH * 32 + 16, 16, 16, 0);
0652     Compress(F + WIDTH * 32 + 32, 16, 16, 0);
0653     BigClear();
0654     while (NumProbs > 0) {
0655         BigPush(ProbBuf[--NumProbs]);
0656     }
0657     BigWrite(fbuf);
0658 }
0659 
0660 void KXFace::Compress(char *f, int wid, int hei, int lev)
0661 {
0662     if (AllWhite(f, wid, hei)) {
0663         RevPush(&levels[lev][WHITE]);
0664         return;
0665     }
0666     if (AllBlack(f, wid, hei)) {
0667         RevPush(&levels[lev][BLACK]);
0668         PushGreys(f, wid, hei);
0669         return;
0670     }
0671     RevPush(&levels[lev][GREY]);
0672     wid /= 2;
0673     hei /= 2;
0674     lev++;
0675     Compress(f, wid, hei, lev);
0676     Compress(f + wid, wid, hei, lev);
0677     Compress(f + hei * WIDTH, wid, hei, lev);
0678     Compress(f + wid + hei * WIDTH, wid, hei, lev);
0679 }
0680 
0681 int KXFace::AllWhite(char *f, int wid, int hei)
0682 {
0683     return (*f == 0) && Same(f, wid, hei);
0684 }
0685 
0686 int KXFace::AllBlack(char *f, int wid, int hei)
0687 {
0688     if (wid > 3) {
0689         wid /= 2;
0690         hei /= 2;
0691         return AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) && AllBlack(f + WIDTH * hei, wid, hei) && AllBlack(f + WIDTH * hei + wid, wid, hei);
0692     } else {
0693         return *f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1);
0694     }
0695 }
0696 
0697 int KXFace::Same(char *f, int wid, int hei)
0698 {
0699     char val;
0700     char *row;
0701     int x;
0702 
0703     val = *f;
0704     while (hei--) {
0705         row = f;
0706         x = wid;
0707         while (x--) {
0708             if (*(row++) != val) {
0709                 return 0;
0710             }
0711         }
0712         f += WIDTH;
0713     }
0714     return 1;
0715 }
0716 
0717 void KXFace::PushGreys(char *f, int wid, int hei)
0718 {
0719     if (wid > 3) {
0720         wid /= 2;
0721         hei /= 2;
0722         PushGreys(f, wid, hei);
0723         PushGreys(f + wid, wid, hei);
0724         PushGreys(f + WIDTH * hei, wid, hei);
0725         PushGreys(f + WIDTH * hei + wid, wid, hei);
0726     } else {
0727         RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) + 8 * *(f + WIDTH + 1));
0728     }
0729 }
0730 
0731 #include "moc_kxface.cpp"