krop's commit fixes my problem in a better way, reverting
[kdepim.git] / libkdepim / kxface.cpp
blobff5366b33cf2fae784081c9ebec709bd0e6a54e1
1 /*
2 This file is part of libkdepim.
4 Original compface:
5 Copyright (c) James Ashton - Sydney University - June 1990. //krazy:exclude=copyright
7 Additions for KDE:
8 Copyright (c) 2004 Jakob Schröter <js@camaya.net>
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public License
21 along with this library; see the file COPYING.LIB. If not, write to
22 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 Boston, MA 02110-1301, USA.
26 #include "kxface.h"
28 #include <KDebug>
30 #include <QBuffer>
31 #include <QImage>
32 #include <QRegExp>
33 #include <QString>
34 #include <QPainter>
36 #include <stdlib.h>
37 #include <string.h>
39 #define GEN(g) F[h] ^= G.g[k]; break
41 #define BITSPERDIG 4
42 #define DIGITS (PIXELS / BITSPERDIG)
43 #define DIGSPERWORD 4
44 #define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG)
46 /* compressed output uses the full range of printable characters.
47 * in ascii these are in a contiguous block so we just need to know
48 * the first and last. The total number of printables is needed too */
49 #define FIRSTPRINT '!'
50 #define LASTPRINT '~'
51 #define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1)
53 /* output line length for compressed data */
54 #define MAXLINELEN 78
56 /* Portable, very large unsigned integer arithmetic is needed.
57 * Implementation uses arrays of WORDs. COMPs must have at least
58 * twice as many bits as WORDs to handle intermediate results */
59 #define COMP unsigned long
60 #define WORDCARRY (1 << BITSPERWORD)
61 #define WORDMASK (WORDCARRY - 1)
63 #define ERR_OK 0 /* successful completion */
64 #define ERR_EXCESS 1 /* completed OK but some input was ignored */
65 #define ERR_INSUFF -1 /* insufficient input. Bad face format? */
66 #define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */
68 #define BLACK 0
69 #define GREY 1
70 #define WHITE 2
72 #define MAX_XFACE_LENGTH 2048
74 using namespace KPIM;
76 KXFace::KXFace()
78 NumProbs = 0;
81 KXFace::~KXFace()
85 QString KXFace::fromImage( const QImage &image )
87 if( image.isNull() )
88 return QString();
90 QImage scaledImg = image.scaled( 48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
91 QByteArray ba;
92 QBuffer buffer( &ba, this );
93 buffer.open( QIODevice::WriteOnly );
94 scaledImg.save( &buffer, "XBM" );
95 QString xbm( ba );
96 xbm.remove( 0, xbm.indexOf( "{" ) + 1 );
97 xbm.truncate( xbm.indexOf( "}" ) );
98 xbm.remove( ' ' );
99 xbm.remove( ',' );
100 xbm.remove( "0x" );
101 xbm.remove( '\n' );
102 xbm.truncate( 576 );
103 QString tmp = xbm.toLatin1();
104 int len = tmp.length();
105 for( int i=0; i<len; ++i )
107 switch( tmp[i].toLatin1() )
109 case '1': tmp[i] = '8'; break;
110 case '2': tmp[i] = '4'; break;
111 case '3': tmp[i] = 'c'; break;
112 case '4': tmp[i] = '2'; break;
113 case '5': tmp[i] = 'a'; break;
114 case '7': tmp[i] = 'e'; break;
115 case '8': tmp[i] = '1'; break;
116 case 'A':
117 case 'a': tmp[i] = '5'; break;
118 case 'B':
119 case 'b': tmp[i] = 'd'; break;
120 case 'C':
121 case 'c': tmp[i] = '3'; break;
122 case 'D':
123 case 'd': tmp[i] = 'b'; break;
124 case 'E':
125 case 'e': tmp[i] = '7'; break;
127 if ( i % 2 )
129 QChar t = tmp[i];
130 tmp[i] = tmp[i-1];
131 tmp[i-1] = t;
134 tmp.replace( QRegExp( "(\\w{12})" ), "\\1\n" );
135 tmp.replace( QRegExp( "(\\w{4})" ), "0x\\1," );
136 len = tmp.length();
137 char *fbuf = (char *)malloc( len + 1 );
138 strncpy( fbuf, (const char *)tmp.toLatin1(), len );
139 fbuf[len] = '\0';
140 if ( !( status = setjmp( comp_env ) ) )
142 ReadFace( fbuf );
143 GenFace();
144 CompAll( fbuf );
146 QString ret( fbuf );
147 free( fbuf );
149 return ret;
152 QImage KXFace::toImage(const QString &xface)
154 if ( xface.length() > MAX_XFACE_LENGTH )
155 return QImage();
157 char *fbuf = (char *)malloc( MAX_XFACE_LENGTH );
158 memset( fbuf, '\0', MAX_XFACE_LENGTH );
159 strncpy( fbuf, xface.toLatin1(), xface.length() );
160 QByteArray img;
161 if ( !( status = setjmp( comp_env ) ) )
163 UnCompAll( fbuf );/* compress otherwise */
164 UnGenFace();
165 img = WriteFace();
167 free( fbuf );
168 QImage p;
169 p.loadFromData( img, "XBM" );
171 return p;
174 //============================================================================
175 // more or less original compface 1.4 source
177 void KXFace::RevPush(const Prob *p)
179 if (NumProbs >= PIXELS * 2 - 1)
180 longjmp(comp_env, ERR_INTERNAL);
181 ProbBuf[NumProbs++] = (Prob *) p;
184 void KXFace::BigPush(Prob *p)
186 static unsigned char tmp;
188 BigDiv(p->p_range, &tmp);
189 BigMul(0);
190 BigAdd(tmp + p->p_offset);
193 int KXFace::BigPop(register const Prob *p)
195 static unsigned char tmp;
196 register int i;
198 BigDiv(0, &tmp);
199 i = 0;
200 while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset))
202 p++;
203 i++;
205 BigMul(p->p_range);
206 BigAdd(tmp - p->p_offset);
207 return i;
211 /* Divide B by a storing the result in B and the remainder in the word
212 * pointer to by r
214 void KXFace::BigDiv(register unsigned char a, register unsigned char *r)
216 register int i;
217 register unsigned char *w;
218 register COMP c, d;
220 a &= WORDMASK;
221 if ((a == 1) || (B.b_words == 0))
223 *r = 0;
224 return;
226 if (a == 0) /* treat this as a == WORDCARRY */
227 { /* and just shift everything right a WORD (unsigned char)*/
228 i = --B.b_words;
229 w = B.b_word;
230 *r = *w;
231 while (i--)
233 *w = *(w + 1);
234 w++;
236 *w = 0;
237 return;
239 w = B.b_word + (i = B.b_words);
240 c = 0;
241 while (i--)
243 c <<= BITSPERWORD;
244 c += (COMP)*--w;
245 d = c / (COMP)a;
246 c = c % (COMP)a;
247 *w = (unsigned char)(d & WORDMASK);
249 *r = c;
250 if (B.b_word[B.b_words - 1] == 0)
251 B.b_words--;
254 /* Multiply a by B storing the result in B
256 void KXFace::BigMul(register unsigned char a)
258 register int i;
259 register unsigned char *w;
260 register COMP c;
262 a &= WORDMASK;
263 if ((a == 1) || (B.b_words == 0))
264 return;
265 if (a == 0) /* treat this as a == WORDCARRY */
266 { /* and just shift everything left a WORD (unsigned char) */
267 if ((i = B.b_words++) >= MAXWORDS - 1)
268 longjmp(comp_env, ERR_INTERNAL);
269 w = B.b_word + i;
270 while (i--)
272 *w = *(w - 1);
273 w--;
275 *w = 0;
276 return;
278 i = B.b_words;
279 w = B.b_word;
280 c = 0;
281 while (i--)
283 c += (COMP)*w * (COMP)a;
284 *(w++) = (unsigned char)(c & WORDMASK);
285 c >>= BITSPERWORD;
287 if (c)
289 if (B.b_words++ >= MAXWORDS)
290 longjmp(comp_env, ERR_INTERNAL);
291 *w = (COMP)(c & WORDMASK);
295 /* Add to a to B storing the result in B
297 void KXFace::BigAdd(unsigned char a)
299 register int i;
300 register unsigned char *w;
301 register COMP c;
303 a &= WORDMASK;
304 if (a == 0)
305 return;
306 i = 0;
307 w = B.b_word;
308 c = a;
309 while ((i < B.b_words) && c)
311 c += (COMP)*w;
312 *w++ = (unsigned char)(c & WORDMASK);
313 c >>= BITSPERWORD;
314 i++;
316 if ((i == B.b_words) && c)
318 if (B.b_words++ >= MAXWORDS)
319 longjmp(comp_env, ERR_INTERNAL);
320 *w = (COMP)(c & WORDMASK);
324 void KXFace::BigClear()
326 B.b_words = 0;
329 QByteArray KXFace::WriteFace()
331 register char *s;
332 register int i, j, bits, digits, words;
333 int digsperword = DIGSPERWORD;
334 int wordsperline = WORDSPERLINE;
335 QByteArray t( "#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n " );
336 j = t.length() - 1;
338 s = F;
339 bits = digits = words = i = 0;
340 t.resize( MAX_XFACE_LENGTH );
341 digsperword = 2;
342 wordsperline = 15;
343 while ( s < F + PIXELS )
345 if ( ( bits == 0 ) && ( digits == 0 ) )
347 t[j++] = '0';
348 t[j++] = 'x';
350 if ( *(s++) )
351 i = ( i >> 1 ) | 0x8;
352 else
353 i >>= 1;
354 if ( ++bits == BITSPERDIG )
356 j++;
357 t[j-( ( digits & 1 ) * 2 )] = *(i + HexDigits);
358 bits = i = 0;
359 if ( ++digits == digsperword )
361 if ( s >= F + PIXELS )
362 break;
363 t[j++] = ',';
364 digits = 0;
365 if ( ++words == wordsperline )
367 t[j++] = '\n';
368 t[j++] = ' ';
369 words = 0;
374 t.resize( j + 1 );
375 t += "};\n";
376 return t;
379 void KXFace::UnCompAll(char *fbuf)
381 register char *p;
383 BigClear();
384 BigRead(fbuf);
385 p = F;
386 while (p < F + PIXELS)
387 *(p++) = 0;
388 UnCompress(F, 16, 16, 0);
389 UnCompress(F + 16, 16, 16, 0);
390 UnCompress(F + 32, 16, 16, 0);
391 UnCompress(F + WIDTH * 16, 16, 16, 0);
392 UnCompress(F + WIDTH * 16 + 16, 16, 16, 0);
393 UnCompress(F + WIDTH * 16 + 32, 16, 16, 0);
394 UnCompress(F + WIDTH * 32, 16, 16, 0);
395 UnCompress(F + WIDTH * 32 + 16, 16, 16, 0);
396 UnCompress(F + WIDTH * 32 + 32, 16, 16, 0);
399 void KXFace::UnCompress(char *f, int wid, int hei, int lev)
401 switch (BigPop(&levels[lev][0]))
403 case WHITE :
404 return;
405 case BLACK :
406 PopGreys(f, wid, hei);
407 return;
408 default :
409 wid /= 2;
410 hei /= 2;
411 lev++;
412 UnCompress(f, wid, hei, lev);
413 UnCompress(f + wid, wid, hei, lev);
414 UnCompress(f + hei * WIDTH, wid, hei, lev);
415 UnCompress(f + wid + hei * WIDTH, wid, hei, lev);
416 return;
420 void KXFace::BigWrite(register char *fbuf)
422 static unsigned char tmp;
423 static char buf[DIGITS];
424 register char *s;
425 register int i;
427 s = buf;
428 while (B.b_words > 0)
430 BigDiv(NUMPRINTS, &tmp);
431 *(s++) = tmp + FIRSTPRINT;
433 i = 7; // leave room for the field name on the first line
434 *(fbuf++) = ' ';
435 while (s-- > buf)
437 if (i == 0)
438 *(fbuf++) = ' ';
439 *(fbuf++) = *s;
440 if (++i >= MAXLINELEN)
442 *(fbuf++) = '\n';
443 i = 0;
446 if (i > 0)
447 *(fbuf++) = '\n';
448 *(fbuf++) = '\0';
451 void KXFace::BigRead(register char *fbuf)
453 register int c;
455 while (*fbuf != '\0')
457 c = *(fbuf++);
458 if ((c < FIRSTPRINT) || (c > LASTPRINT))
459 continue;
460 BigMul(NUMPRINTS);
461 BigAdd((unsigned char)(c - FIRSTPRINT));
465 void KXFace::ReadFace(char *fbuf)
467 register int c, i;
468 register char *s, *t;
470 t = s = fbuf;
471 for(i = strlen(s); i > 0; i--)
473 c = (int)*(s++);
474 if ((c >= '0') && (c <= '9'))
476 if (t >= fbuf + DIGITS)
478 status = ERR_EXCESS;
479 break;
481 *(t++) = c - '0';
483 else if ((c >= 'A') && (c <= 'F'))
485 if (t >= fbuf + DIGITS)
487 status = ERR_EXCESS;
488 break;
490 *(t++) = c - 'A' + 10;
492 else if ((c >= 'a') && (c <= 'f'))
494 if (t >= fbuf + DIGITS)
496 status = ERR_EXCESS;
497 break;
499 *(t++) = c - 'a' + 10;
501 else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t-1) == 0))
502 t--;
504 if (t < fbuf + DIGITS)
505 longjmp(comp_env, ERR_INSUFF);
506 s = fbuf;
507 t = F;
508 c = 1 << (BITSPERDIG - 1);
509 while (t < F + PIXELS)
511 *(t++) = (*s & c) ? 1 : 0;
512 if ((c >>= 1) == 0)
514 s++;
515 c = 1 << (BITSPERDIG - 1);
520 void KXFace::GenFace()
522 static char newp[PIXELS];
523 register char *f1;
524 register char *f2;
525 register int i;
527 f1 = newp;
528 f2 = F;
529 i = PIXELS;
530 while (i-- > 0)
531 *(f1++) = *(f2++);
532 Gen(newp);
535 void KXFace::UnGenFace()
537 Gen(F);
540 // static
541 void KXFace::Gen(register char *f)
543 register int m, l, k, j, i, h;
545 for (j = 0; j < HEIGHT; j++)
547 for (i = 0; i < WIDTH; i++)
549 h = i + j * WIDTH;
550 k = 0;
551 for (l = i - 2; l <= i + 2; l++)
552 for (m = j - 2; m <= j; m++)
554 if ((l >= i) && (m == j))
555 continue;
556 if ((l > 0) && (l <= WIDTH) && (m > 0))
557 k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2;
559 switch (i)
561 case 1 :
562 switch (j)
564 case 1 : GEN(g_22);
565 case 2 : GEN(g_21);
566 default : GEN(g_20);
568 break;
569 case 2 :
570 switch (j)
572 case 1 : GEN(g_12);
573 case 2 : GEN(g_11);
574 default : GEN(g_10);
576 break;
577 case WIDTH - 1 :
578 switch (j)
580 case 1 : GEN(g_42);
581 case 2 : GEN(g_41);
582 default : GEN(g_40);
584 break;
585 /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in
586 because it appears exactly like this in the original compface code.
587 case WIDTH :
588 switch (j)
590 case 1 : GEN(g_32);
591 case 2 : GEN(g_31);
592 default : GEN(g_30);
594 break;
596 default :
597 switch (j)
599 case 1 : GEN(g_02);
600 case 2 : GEN(g_01);
601 default : GEN(g_00);
603 break;
609 void KXFace::PopGreys(char *f, int wid, int hei)
611 if (wid > 3)
613 wid /= 2;
614 hei /= 2;
615 PopGreys(f, wid, hei);
616 PopGreys(f + wid, wid, hei);
617 PopGreys(f + WIDTH * hei, wid, hei);
618 PopGreys(f + WIDTH * hei + wid, wid, hei);
620 else
622 wid = BigPop(freqs);
623 if (wid & 1)
624 *f = 1;
625 if (wid & 2)
626 *(f + 1) = 1;
627 if (wid & 4)
628 *(f + WIDTH) = 1;
629 if (wid & 8)
630 *(f + WIDTH + 1) = 1;
634 void KXFace::CompAll(char *fbuf)
636 Compress(F, 16, 16, 0);
637 Compress(F + 16, 16, 16, 0);
638 Compress(F + 32, 16, 16, 0);
639 Compress(F + WIDTH * 16, 16, 16, 0);
640 Compress(F + WIDTH * 16 + 16, 16, 16, 0);
641 Compress(F + WIDTH * 16 + 32, 16, 16, 0);
642 Compress(F + WIDTH * 32, 16, 16, 0);
643 Compress(F + WIDTH * 32 + 16, 16, 16, 0);
644 Compress(F + WIDTH * 32 + 32, 16, 16, 0);
645 BigClear();
646 while (NumProbs > 0)
647 BigPush(ProbBuf[--NumProbs]);
648 BigWrite(fbuf);
651 void KXFace::Compress(register char *f, register int wid, register int hei, register int lev)
653 if (AllWhite(f, wid, hei))
655 RevPush(&levels[lev][WHITE]);
656 return;
658 if (AllBlack(f, wid, hei))
660 RevPush(&levels[lev][BLACK]);
661 PushGreys(f, wid, hei);
662 return;
664 RevPush(&levels[lev][GREY]);
665 wid /= 2;
666 hei /= 2;
667 lev++;
668 Compress(f, wid, hei, lev);
669 Compress(f + wid, wid, hei, lev);
670 Compress(f + hei * WIDTH, wid, hei, lev);
671 Compress(f + wid + hei * WIDTH, wid, hei, lev);
674 int KXFace::AllWhite(char *f, int wid, int hei)
676 return ((*f == 0) && Same(f, wid, hei));
679 int KXFace::AllBlack(char *f, int wid, int hei)
681 if (wid > 3)
683 wid /= 2;
684 hei /= 2;
685 return (AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) &&
686 AllBlack(f + WIDTH * hei, wid, hei) &&
687 AllBlack(f + WIDTH * hei + wid, wid, hei));
689 else
690 return (*f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1));
693 int KXFace::Same(register char *f, register int wid, register int hei)
695 register char val, *row;
696 register int x;
698 val = *f;
699 while (hei--)
701 row = f;
702 x = wid;
703 while (x--)
704 if (*(row++) != val)
705 return(0);
706 f += WIDTH;
708 return 1;
711 void KXFace::PushGreys(char *f, int wid, int hei)
713 if (wid > 3)
715 wid /= 2;
716 hei /= 2;
717 PushGreys(f, wid, hei);
718 PushGreys(f + wid, wid, hei);
719 PushGreys(f + WIDTH * hei, wid, hei);
720 PushGreys(f + WIDTH * hei + wid, wid, hei);
722 else
723 RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) +
724 8 * *(f + WIDTH + 1));
728 #include "kxface.moc"