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 <[email protected]>
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 */
40 static 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 
58 static const int MAX_XFACE_LENGTH = 2048;
59 
60 using namespace MessageViewer;
61 
62 KXFace::KXFace()
63 {
64  NumProbs = 0;
65 }
66 
67 KXFace::~KXFace() = default;
68 
70 {
71  if (image.isNull()) {
72  return {};
73  }
74 
75  QImage scaledImg = image.scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
76  QByteArray ba;
77  QBuffer buffer(&ba, this);
78  buffer.open(QIODevice::WriteOnly);
79  scaledImg.save(&buffer, "XBM");
81  xbm.remove(0, xbm.indexOf(QLatin1String("{")) + 1);
82  xbm.truncate(xbm.indexOf(QLatin1String("}")));
83  xbm.remove(QLatin1Char(' '));
84  xbm.remove(QLatin1Char(','));
85  xbm.remove(QStringLiteral("0x"));
86  xbm.remove(QLatin1Char('\n'));
87  xbm.truncate(576);
88  QString tmp = QLatin1String(xbm.toLatin1());
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 
182 void 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 
190 void 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 
199 int 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  */
218 void 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  */
258 void 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  */
299 void 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 
326 void KXFace::BigClear()
327 {
328  B.b_words = 0;
329 }
330 
331 QByteArray 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 
382 void 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 
403 void 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 
423 void 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 
453 void 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 
467 void 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 
514 void 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 
530 void KXFace::UnGenFace()
531 {
532  Gen(F);
533 }
534 
535 // static
536 void 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 
616 void 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 
642 void 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 
660 void 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 
681 int KXFace::AllWhite(char *f, int wid, int hei)
682 {
683  return (*f == 0) && Same(f, wid, hei);
684 }
685 
686 int 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 
697 int 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 
717 void 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 }
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool loadFromData(const uchar *data, int len, const char *format)
void truncate(int position)
bool save(const QString &fileName, const char *format, int quality) const const
bool isNull() const const
int length() const const
QString & remove(int position, int n)
void resize(int size)
QImage toImage(const QString &xface)
creates a pixmap from xface
Definition: kxface.cpp:157
const char * constData() const const
QString fromImage(const QImage &image)
generates the xface string from image
Definition: kxface.cpp:69
virtual bool open(QIODevice::OpenMode flags) override
QString & replace(int position, int n, QChar after)
QByteArray toLatin1() const const
IgnoreAspectRatio
int length() const const
QString fromLatin1(const char *str, int size)
SmoothTransformation
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Fri Nov 26 2021 23:16:42 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.