libtdepim

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