Messagelib

kxface.cpp
1/*
2 This file is part of libkdepim.
3
4 Original compface:
5 Copyright (c) James Ashton - Sydney University - June 1990. //krazy:exclude=copyright
6
7 Additions for KDE:
8 SPDX-FileCopyrightText: 2004 Jakob Schröter <js@camaya.net>
9
10 SPDX-License-Identifier: LGPL-2.0-or-later
11*/
12
13#include "kxface.h"
14
15#include <QBuffer>
16#include <QImage>
17#include <QRegularExpression>
18#include <QString>
19
20#include <cstdlib>
21#include <cstring>
22
23#define GEN(g) \
24 F[h] ^= G.g[k]; \
25 break
26
27#define BITSPERDIG 4
28#define DIGITS (PIXELS / BITSPERDIG)
29#define DIGSPERWORD 4
30#define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG)
31
32/* compressed output uses the full range of printable characters.
33 * in ascii these are in a contiguous block so we just need to know
34 * the first and last. The total number of printables is needed too */
35#define FIRSTPRINT '!'
36#define LASTPRINT '~'
37#define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1)
38
39/* output line length for compressed data */
40static const int MAXLINELEN = 78;
41
42/* Portable, very large unsigned integer arithmetic is needed.
43 * Implementation uses arrays of WORDs. COMPs must have at least
44 * twice as many bits as WORDs to handle intermediate results */
45#define COMP unsigned long
46#define WORDCARRY (1 << BITSPERWORD)
47#define WORDMASK (WORDCARRY - 1)
48
49#define ERR_OK 0 /* successful completion */
50#define ERR_EXCESS 1 /* completed OK but some input was ignored */
51#define ERR_INSUFF -1 /* insufficient input. Bad face format? */
52#define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */
53
54#define BLACK 0
55#define GREY 1
56#define WHITE 2
57
58static const int MAX_XFACE_LENGTH = 2048;
59
60using namespace MessageViewer;
61
62KXFace::KXFace()
63{
64 NumProbs = 0;
65}
66
67KXFace::~KXFace() = default;
68
70{
71 if (image.isNull()) {
72 return {};
73 }
74
76 QByteArray ba;
77 QBuffer buffer(&ba, this);
79 scaledImg.save(&buffer, "XBM");
81 xbm.remove(0, xbm.indexOf(QLatin1StringView("{")) + 1);
82 xbm.truncate(xbm.indexOf(QLatin1StringView("}")));
83 xbm.remove(QLatin1Char(' '));
84 xbm.remove(QLatin1Char(','));
85 xbm.remove(QStringLiteral("0x"));
86 xbm.remove(QLatin1Char('\n'));
87 xbm.truncate(576);
89 int len = tmp.length();
90 for (int i = 0; i < len; ++i) {
91 switch (tmp[i].toLatin1()) {
92 case '1':
93 tmp[i] = QLatin1Char('8');
94 break;
95 case '2':
96 tmp[i] = QLatin1Char('4');
97 break;
98 case '3':
99 tmp[i] = QLatin1Char('c');
100 break;
101 case '4':
102 tmp[i] = QLatin1Char('2');
103 break;
104 case '5':
105 tmp[i] = QLatin1Char('a');
106 break;
107 case '7':
108 tmp[i] = QLatin1Char('e');
109 break;
110 case '8':
111 tmp[i] = QLatin1Char('1');
112 break;
113 case 'A':
114 case 'a':
115 tmp[i] = QLatin1Char('5');
116 break;
117 case 'B':
118 case 'b':
119 tmp[i] = QLatin1Char('d');
120 break;
121 case 'C':
122 case 'c':
123 tmp[i] = QLatin1Char('3');
124 break;
125 case 'D':
126 case 'd':
127 tmp[i] = QLatin1Char('b');
128 break;
129 case 'E':
130 case 'e':
131 tmp[i] = QLatin1Char('7');
132 break;
133 }
134 if (i % 2) {
135 QChar t = tmp[i];
136 tmp[i] = tmp[i - 1];
137 tmp[i - 1] = t;
138 }
139 }
140 tmp.replace(QRegularExpression(QStringLiteral("(\\w{12})")), QStringLiteral("\\1\n"));
141 tmp.replace(QRegularExpression(QStringLiteral("(\\w{4})")), QStringLiteral("0x\\1,"));
142 len = tmp.length();
143 char *fbuf = (char *)malloc(len + 1);
144 strncpy(fbuf, tmp.toLatin1().constData(), len);
145 fbuf[len] = '\0';
146 if (!(status = setjmp(comp_env))) {
147 ReadFace(fbuf);
148 GenFace();
149 CompAll(fbuf);
150 }
151 QString ret(QString::fromLatin1(fbuf));
152 free(fbuf);
153
154 return ret;
155}
156
158{
159 if (xface.length() > MAX_XFACE_LENGTH) {
160 return {};
161 }
162
163 char *fbuf = (char *)malloc(MAX_XFACE_LENGTH);
164 memset(fbuf, '\0', MAX_XFACE_LENGTH);
165 strncpy(fbuf, xface.toLatin1().constData(), xface.length());
166 QByteArray img;
167 if (!(status = setjmp(comp_env))) {
168 UnCompAll(fbuf); /* compress otherwise */
169 UnGenFace();
170 img = WriteFace();
171 }
172 free(fbuf);
173 QImage p;
174 p.loadFromData(img, "XBM");
175
176 return p;
177}
178
179//============================================================================
180// more or less original compface 1.4 source
181
182void KXFace::RevPush(const Prob *p)
183{
184 if (NumProbs >= PIXELS * 2 - 1) {
185 longjmp(comp_env, ERR_INTERNAL);
186 }
187 ProbBuf[NumProbs++] = (Prob *)p;
188}
189
190void KXFace::BigPush(Prob *p)
191{
192 static unsigned char tmp;
193
194 BigDiv(p->p_range, &tmp);
195 BigMul(0);
196 BigAdd(tmp + p->p_offset);
197}
198
199int KXFace::BigPop(const Prob *p)
200{
201 static unsigned char tmp;
202 int i;
203
204 BigDiv(0, &tmp);
205 i = 0;
206 while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) {
207 p++;
208 ++i;
209 }
210 BigMul(p->p_range);
211 BigAdd(tmp - p->p_offset);
212 return i;
213}
214
215/* Divide B by a storing the result in B and the remainder in the word
216 * pointer to by r
217 */
218void KXFace::BigDiv(unsigned char a, unsigned char *r)
219{
220 int i;
221 unsigned char *w;
222 COMP c, d;
223
224 a &= WORDMASK;
225 if ((a == 1) || (B.b_words == 0)) {
226 *r = 0;
227 return;
228 }
229 if (a == 0) { /* treat this as a == WORDCARRY */
230 /* and just shift everything right a WORD (unsigned char)*/
231 i = --B.b_words;
232 w = B.b_word;
233 *r = *w;
234 while (i--) {
235 *w = *(w + 1);
236 w++;
237 }
238 *w = 0;
239 return;
240 }
241 w = B.b_word + (i = B.b_words);
242 c = 0;
243 while (i--) {
244 c <<= BITSPERWORD;
245 c += (COMP) * --w;
246 d = c / (COMP)a;
247 c = c % (COMP)a;
248 *w = (unsigned char)(d & WORDMASK);
249 }
250 *r = c;
251 if (B.b_word[B.b_words - 1] == 0) {
252 B.b_words--;
253 }
254}
255
256/* Multiply a by B storing the result in B
257 */
258void KXFace::BigMul(unsigned char a)
259{
260 int i;
261 unsigned char *w;
262 COMP c;
263
264 a &= WORDMASK;
265 if ((a == 1) || (B.b_words == 0)) {
266 return;
267 }
268 if (a == 0) { /* treat this as a == WORDCARRY */
269 /* and just shift everything left a WORD (unsigned char) */
270 if ((i = B.b_words++) >= MAXWORDS - 1) {
271 longjmp(comp_env, ERR_INTERNAL);
272 }
273 w = B.b_word + i;
274 while (i--) {
275 *w = *(w - 1);
276 w--;
277 }
278 *w = 0;
279 return;
280 }
281 i = B.b_words;
282 w = B.b_word;
283 c = 0;
284 while (i--) {
285 c += (COMP)*w * (COMP)a;
286 *(w++) = (unsigned char)(c & WORDMASK);
287 c >>= BITSPERWORD;
288 }
289 if (c) {
290 if (B.b_words++ >= MAXWORDS) {
291 longjmp(comp_env, ERR_INTERNAL);
292 }
293 *w = (COMP)(c & WORDMASK);
294 }
295}
296
297/* Add to a to B storing the result in B
298 */
299void KXFace::BigAdd(unsigned char a)
300{
301 int i;
302 unsigned char *w;
303 COMP c;
304
305 a &= WORDMASK;
306 if (a == 0) {
307 return;
308 }
309 i = 0;
310 w = B.b_word;
311 c = a;
312 while ((i < B.b_words) && c) {
313 c += (COMP)*w;
314 *w++ = (unsigned char)(c & WORDMASK);
315 c >>= BITSPERWORD;
316 ++i;
317 }
318 if ((i == B.b_words) && c) {
319 if (B.b_words++ >= MAXWORDS) {
320 longjmp(comp_env, ERR_INTERNAL);
321 }
322 *w = (COMP)(c & WORDMASK);
323 }
324}
325
326void KXFace::BigClear()
327{
328 B.b_words = 0;
329}
330
331QByteArray KXFace::WriteFace()
332{
333 char *s;
334 int i;
335 int j;
336 int bits;
337 int digits;
338 int words;
339 // int digsperword = DIGSPERWORD;
340 // int wordsperline = WORDSPERLINE;
341 QByteArray t("#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n ");
342 j = t.length() - 1;
343
344 s = F;
345 bits = digits = words = i = 0;
346 t.resize(MAX_XFACE_LENGTH);
347 int digsperword = 2;
348 int wordsperline = 15;
349 while (s < F + PIXELS) {
350 if ((bits == 0) && (digits == 0)) {
351 t[j++] = '0';
352 t[j++] = 'x';
353 }
354 if (*(s++)) {
355 i = (i >> 1) | 0x8;
356 } else {
357 i >>= 1;
358 }
359 if (++bits == BITSPERDIG) {
360 j++;
361 t[j - ((digits & 1) * 2)] = *(i + HexDigits);
362 bits = i = 0;
363 if (++digits == digsperword) {
364 if (s >= F + PIXELS) {
365 break;
366 }
367 t[j++] = ',';
368 digits = 0;
369 if (++words == wordsperline) {
370 t[j++] = '\n';
371 t[j++] = ' ';
372 words = 0;
373 }
374 }
375 }
376 }
377 t.resize(j + 1);
378 t += "};\n";
379 return t;
380}
381
382void KXFace::UnCompAll(char *fbuf)
383{
384 char *p;
385
386 BigClear();
387 BigRead(fbuf);
388 p = F;
389 while (p < F + PIXELS) {
390 *(p++) = 0;
391 }
392 UnCompress(F, 16, 16, 0);
393 UnCompress(F + 16, 16, 16, 0);
394 UnCompress(F + 32, 16, 16, 0);
395 UnCompress(F + WIDTH * 16, 16, 16, 0);
396 UnCompress(F + WIDTH * 16 + 16, 16, 16, 0);
397 UnCompress(F + WIDTH * 16 + 32, 16, 16, 0);
398 UnCompress(F + WIDTH * 32, 16, 16, 0);
399 UnCompress(F + WIDTH * 32 + 16, 16, 16, 0);
400 UnCompress(F + WIDTH * 32 + 32, 16, 16, 0);
401}
402
403void KXFace::UnCompress(char *f, int wid, int hei, int lev)
404{
405 switch (BigPop(&levels[lev][0])) {
406 case WHITE:
407 return;
408 case BLACK:
409 PopGreys(f, wid, hei);
410 return;
411 default:
412 wid /= 2;
413 hei /= 2;
414 lev++;
415 UnCompress(f, wid, hei, lev);
416 UnCompress(f + wid, wid, hei, lev);
417 UnCompress(f + hei * WIDTH, wid, hei, lev);
418 UnCompress(f + wid + hei * WIDTH, wid, hei, lev);
419 return;
420 }
421}
422
423void KXFace::BigWrite(char *fbuf)
424{
425 static unsigned char tmp;
426 static char buf[DIGITS];
427 char *s;
428 int i;
429
430 s = buf;
431 while (B.b_words > 0) {
432 BigDiv(NUMPRINTS, &tmp);
433 *(s++) = tmp + FIRSTPRINT;
434 }
435 i = 7; // leave room for the field name on the first line
436 *(fbuf++) = ' ';
437 while (s-- > buf) {
438 if (i == 0) {
439 *(fbuf++) = ' ';
440 }
441 *(fbuf++) = *s;
442 if (++i >= MAXLINELEN) {
443 *(fbuf++) = '\n';
444 i = 0;
445 }
446 }
447 if (i > 0) {
448 *(fbuf++) = '\n';
449 }
450 *(fbuf++) = '\0';
451}
452
453void KXFace::BigRead(char *fbuf)
454{
455 int c;
456
457 while (*fbuf != '\0') {
458 c = *(fbuf++);
459 if ((c < FIRSTPRINT) || (c > LASTPRINT)) {
460 continue;
461 }
462 BigMul(NUMPRINTS);
463 BigAdd((unsigned char)(c - FIRSTPRINT));
464 }
465}
466
467void KXFace::ReadFace(char *fbuf)
468{
469 int c;
470 int i;
471 char *s;
472 char *t;
473
474 t = s = fbuf;
475 for (i = strlen(s); i > 0; --i) {
476 c = (int)*(s++);
477 if ((c >= '0') && (c <= '9')) {
478 if (t >= fbuf + DIGITS) {
479 status = ERR_EXCESS;
480 break;
481 }
482 *(t++) = c - '0';
483 } else if ((c >= 'A') && (c <= 'F')) {
484 if (t >= fbuf + DIGITS) {
485 status = ERR_EXCESS;
486 break;
487 }
488 *(t++) = c - 'A' + 10;
489 } else if ((c >= 'a') && (c <= 'f')) {
490 if (t >= fbuf + DIGITS) {
491 status = ERR_EXCESS;
492 break;
493 }
494 *(t++) = c - 'a' + 10;
495 } else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t - 1) == 0)) {
496 t--;
497 }
498 }
499 if (t < fbuf + DIGITS) {
500 longjmp(comp_env, ERR_INSUFF);
501 }
502 s = fbuf;
503 t = F;
504 c = 1 << (BITSPERDIG - 1);
505 while (t < F + PIXELS) {
506 *(t++) = (*s & c) ? 1 : 0;
507 if ((c >>= 1) == 0) {
508 s++;
509 c = 1 << (BITSPERDIG - 1);
510 }
511 }
512}
513
514void KXFace::GenFace()
515{
516 static char newp[PIXELS];
517 char *f1;
518 char *f2;
519 int i;
520
521 f1 = newp;
522 f2 = F;
523 i = PIXELS;
524 while (i-- > 0) {
525 *(f1++) = *(f2++);
526 }
527 Gen(newp);
528}
529
530void KXFace::UnGenFace()
531{
532 Gen(F);
533}
534
535// static
536void KXFace::Gen(char *f)
537{
538 int m;
539 int l;
540 int k;
541 int j;
542 int i;
543 int h;
544
545 for (j = 0; j < HEIGHT; ++j) {
546 for (i = 0; i < WIDTH; ++i) {
547 h = i + j * WIDTH;
548 k = 0;
549 for (l = i - 2; l <= i + 2; ++l) {
550 for (m = j - 2; m <= j; ++m) {
551 if ((l >= i) && (m == j)) {
552 continue;
553 }
554 if ((l > 0) && (l <= WIDTH) && (m > 0)) {
555 k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2;
556 }
557 }
558 }
559 switch (i) {
560 case 1:
561 switch (j) {
562 case 1:
563 GEN(g_22);
564 case 2:
565 GEN(g_21);
566 default:
567 GEN(g_20);
568 }
569 break;
570 case 2:
571 switch (j) {
572 case 1:
573 GEN(g_12);
574 case 2:
575 GEN(g_11);
576 default:
577 GEN(g_10);
578 }
579 break;
580 case WIDTH - 1:
581 switch (j) {
582 case 1:
583 GEN(g_42);
584 case 2:
585 GEN(g_41);
586 default:
587 GEN(g_40);
588 }
589 break;
590 /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in
591 because it appears exactly like this in the original compface code.
592 case WIDTH :
593 switch (j)
594 {
595 case 1 : GEN(g_32);
596 case 2 : GEN(g_31);
597 default : GEN(g_30);
598 }
599 break;
600 */
601 default:
602 switch (j) {
603 case 1:
604 GEN(g_02);
605 case 2:
606 GEN(g_01);
607 default:
608 GEN(g_00);
609 }
610 break;
611 }
612 }
613 }
614}
615
616void KXFace::PopGreys(char *f, int wid, int hei)
617{
618 if (wid > 3) {
619 wid /= 2;
620 hei /= 2;
621 PopGreys(f, wid, hei);
622 PopGreys(f + wid, wid, hei);
623 PopGreys(f + WIDTH * hei, wid, hei);
624 PopGreys(f + WIDTH * hei + wid, wid, hei);
625 } else {
626 wid = BigPop(freqs);
627 if (wid & 1) {
628 *f = 1;
629 }
630 if (wid & 2) {
631 *(f + 1) = 1;
632 }
633 if (wid & 4) {
634 *(f + WIDTH) = 1;
635 }
636 if (wid & 8) {
637 *(f + WIDTH + 1) = 1;
638 }
639 }
640}
641
642void KXFace::CompAll(char *fbuf)
643{
644 Compress(F, 16, 16, 0);
645 Compress(F + 16, 16, 16, 0);
646 Compress(F + 32, 16, 16, 0);
647 Compress(F + WIDTH * 16, 16, 16, 0);
648 Compress(F + WIDTH * 16 + 16, 16, 16, 0);
649 Compress(F + WIDTH * 16 + 32, 16, 16, 0);
650 Compress(F + WIDTH * 32, 16, 16, 0);
651 Compress(F + WIDTH * 32 + 16, 16, 16, 0);
652 Compress(F + WIDTH * 32 + 32, 16, 16, 0);
653 BigClear();
654 while (NumProbs > 0) {
655 BigPush(ProbBuf[--NumProbs]);
656 }
657 BigWrite(fbuf);
658}
659
660void KXFace::Compress(char *f, int wid, int hei, int lev)
661{
662 if (AllWhite(f, wid, hei)) {
663 RevPush(&levels[lev][WHITE]);
664 return;
665 }
666 if (AllBlack(f, wid, hei)) {
667 RevPush(&levels[lev][BLACK]);
668 PushGreys(f, wid, hei);
669 return;
670 }
671 RevPush(&levels[lev][GREY]);
672 wid /= 2;
673 hei /= 2;
674 lev++;
675 Compress(f, wid, hei, lev);
676 Compress(f + wid, wid, hei, lev);
677 Compress(f + hei * WIDTH, wid, hei, lev);
678 Compress(f + wid + hei * WIDTH, wid, hei, lev);
679}
680
681int KXFace::AllWhite(char *f, int wid, int hei)
682{
683 return (*f == 0) && Same(f, wid, hei);
684}
685
686int KXFace::AllBlack(char *f, int wid, int hei)
687{
688 if (wid > 3) {
689 wid /= 2;
690 hei /= 2;
691 return AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) && AllBlack(f + WIDTH * hei, wid, hei) && AllBlack(f + WIDTH * hei + wid, wid, hei);
692 } else {
693 return *f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1);
694 }
695}
696
697int KXFace::Same(char *f, int wid, int hei)
698{
699 char val;
700 char *row;
701 int x;
702
703 val = *f;
704 while (hei--) {
705 row = f;
706 x = wid;
707 while (x--) {
708 if (*(row++) != val) {
709 return 0;
710 }
711 }
712 f += WIDTH;
713 }
714 return 1;
715}
716
717void KXFace::PushGreys(char *f, int wid, int hei)
718{
719 if (wid > 3) {
720 wid /= 2;
721 hei /= 2;
722 PushGreys(f, wid, hei);
723 PushGreys(f + wid, wid, hei);
724 PushGreys(f + WIDTH * hei, wid, hei);
725 PushGreys(f + WIDTH * hei + wid, wid, hei);
726 } else {
727 RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) + 8 * *(f + WIDTH + 1));
728 }
729}
730
731#include "moc_kxface.cpp"
QString fromImage(const QImage &image)
generates the xface string from image
Definition kxface.cpp:69
QImage toImage(const QString &xface)
creates a pixmap from xface
Definition kxface.cpp:157
virtual bool open(OpenMode flags) override
const char * constData() const const
bool isNull() const const
bool loadFromData(QByteArrayView data, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString fromLatin1(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
qsizetype length() const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QByteArray toLatin1() const const
void truncate(qsizetype position)
IgnoreAspectRatio
SmoothTransformation
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:33:26 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.