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 
75 using namespace KPIM;
76 
77 KXFace::KXFace()
78 {
79  NumProbs = 0;
80 }
81 
82 KXFace::~KXFace()
83 {
84 }
85 
86 TQString 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 
153 TQImage 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 
178 void 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 
185 void 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 
194 int 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  */
215 void 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  */
257 void 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  */
298 void 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 
325 void KXFace::BigClear()
326 {
327  B.b_words = 0;
328 }
329 
330 TQCString 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 
380 void 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 
400 void 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 
421 void 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 
452 void 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 
466 void 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 
521 void 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 
536 void KXFace::UnGenFace()
537 {
538  Gen(F);
539 }
540 
541 // static
542 void 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 
610 void 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 
635 void 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 
652 void 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 
675 int KXFace::AllWhite(char *f, int wid, int hei)
676 {
677  return ((*f == 0) && Same(f, wid, hei));
678 }
679 
680 int 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 
694 int 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 
712 void 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.