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"