kxface.cpp
00001 /* 00002 This file is part of libkdepim. 00003 00004 Original compface: 00005 Copyright (c) James Ashton - Sydney University - June 1990. 00006 00007 Additions for KDE: 00008 Copyright (c) 2004 Jakob Schröter <js@camaya.net> 00009 00010 This library is free software; you can redistribute it and/or 00011 modify it under the terms of the GNU Library General Public 00012 License as published by the Free Software Foundation; either 00013 version 2 of the License, or (at your option) any later version. 00014 00015 This library is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 Library General Public License for more details. 00019 00020 You should have received a copy of the GNU Library General Public License 00021 along with this library; see the file COPYING.LIB. If not, write to 00022 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00023 Boston, MA 02110-1301, USA. 00024 */ 00025 00026 #include "kxface.h" 00027 00028 #include <kdebug.h> 00029 00030 #include <tqbuffer.h> 00031 #include <tqcstring.h> 00032 #include <tqimage.h> 00033 #include <tqregexp.h> 00034 #include <tqstring.h> 00035 #include <tqpainter.h> 00036 00037 #include <stdlib.h> 00038 #include <string.h> 00039 00040 #define GEN(g) F[h] ^= G.g[k]; break 00041 00042 #define BITSPERDIG 4 00043 #define DIGITS (PIXELS / BITSPERDIG) 00044 #define DIGSPERWORD 4 00045 #define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG) 00046 00047 /* compressed output uses the full range of printable characters. 00048 * in ascii these are in a contiguous block so we just need to know 00049 * the first and last. The total number of printables is needed too */ 00050 #define FIRSTPRINT '!' 00051 #define LASTPRINT '~' 00052 #define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1) 00053 00054 /* output line length for compressed data */ 00055 #define MAXLINELEN 78 00056 00057 /* Portable, very large unsigned integer arithmetic is needed. 00058 * Implementation uses arrays of WORDs. COMPs must have at least 00059 * twice as many bits as WORDs to handle intermediate results */ 00060 #define COMP unsigned long 00061 #define WORDCARRY (1 << BITSPERWORD) 00062 #define WORDMASK (WORDCARRY - 1) 00063 00064 #define ERR_OK 0 /* successful completion */ 00065 #define ERR_EXCESS 1 /* completed OK but some input was ignored */ 00066 #define ERR_INSUFF -1 /* insufficient input. Bad face format? */ 00067 #define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */ 00068 00069 #define BLACK 0 00070 #define GREY 1 00071 #define WHITE 2 00072 00073 #define MAX_XFACE_LENGTH 2048 00074 00075 using namespace KPIM; 00076 00077 KXFace::KXFace() 00078 { 00079 NumProbs = 0; 00080 } 00081 00082 KXFace::~KXFace() 00083 { 00084 } 00085 00086 TQString KXFace::fromImage( const TQImage &image ) 00087 { 00088 if( image.isNull() ) 00089 return TQString(); 00090 00091 TQImage scaledImg = image.smoothScale( 48, 48 ); 00092 TQByteArray ba; 00093 TQBuffer buffer( ba ); 00094 buffer.open( IO_WriteOnly ); 00095 scaledImg.save( &buffer, "XBM" ); 00096 TQString xbm( ba ); 00097 xbm.remove( 0, xbm.find( "{" ) + 1 ); 00098 xbm.truncate( xbm.find( "}" ) ); 00099 xbm.remove( " " ); 00100 xbm.remove( "," ); 00101 xbm.remove( "0x" ); 00102 xbm.remove( "\n" ); 00103 xbm.truncate( 576 ); 00104 TQCString tmp = TQCString( xbm.latin1() ); 00105 uint len = tmp.length(); 00106 for( uint i=0; i<len; ++i ) 00107 { 00108 switch( tmp[i] ) 00109 { 00110 case '1': tmp[i] = '8'; break; 00111 case '2': tmp[i] = '4'; break; 00112 case '3': tmp[i] = 'c'; break; 00113 case '4': tmp[i] = '2'; break; 00114 case '5': tmp[i] = 'a'; break; 00115 case '7': tmp[i] = 'e'; break; 00116 case '8': tmp[i] = '1'; break; 00117 case 'A': 00118 case 'a': tmp[i] = '5'; break; 00119 case 'B': 00120 case 'b': tmp[i] = 'd'; break; 00121 case 'C': 00122 case 'c': tmp[i] = '3'; break; 00123 case 'D': 00124 case 'd': tmp[i] = 'b'; break; 00125 case 'E': 00126 case 'e': tmp[i] = '7'; break; 00127 } 00128 if ( i % 2 ) 00129 { 00130 char t = tmp[i]; 00131 tmp[i] = tmp[i-1]; 00132 tmp[i-1] = t; 00133 } 00134 } 00135 tmp.replace( TQRegExp( "(\\w{12})" ), "\\1\n" ); 00136 tmp.replace( TQRegExp( "(\\w{4})" ), "0x\\1," ); 00137 len = tmp.length(); 00138 char *fbuf = (char *)malloc( len + 1 ); 00139 strncpy( fbuf, (const char *)tmp, len ); 00140 fbuf[len] = '\0'; 00141 if ( !( status = setjmp( comp_env ) ) ) 00142 { 00143 ReadFace( fbuf ); 00144 GenFace(); 00145 CompAll( fbuf ); 00146 } 00147 TQString ret( fbuf ); 00148 free( fbuf ); 00149 00150 return ret; 00151 } 00152 00153 TQImage KXFace::toImage(const TQString &xface) 00154 { 00155 if ( xface.length() > MAX_XFACE_LENGTH ) 00156 return TQImage(); 00157 00158 char *fbuf = (char *)malloc( MAX_XFACE_LENGTH ); 00159 memset( fbuf, '\0', MAX_XFACE_LENGTH ); 00160 strncpy( fbuf, xface.latin1(), xface.length() ); 00161 TQCString img; 00162 if ( !( status = setjmp( comp_env ) ) ) 00163 { 00164 UnCompAll( fbuf );/* compress otherwise */ 00165 UnGenFace(); 00166 img = WriteFace(); 00167 } 00168 free( fbuf ); 00169 TQImage p; 00170 p.loadFromData( img, "XBM" ); 00171 00172 return p; 00173 } 00174 00175 //============================================================================ 00176 // more or less original compface 1.4 source 00177 00178 void KXFace::RevPush(const Prob *p) 00179 { 00180 if (NumProbs >= PIXELS * 2 - 1) 00181 longjmp(comp_env, ERR_INTERNAL); 00182 ProbBuf[NumProbs++] = (Prob *) p; 00183 } 00184 00185 void KXFace::BigPush(Prob *p) 00186 { 00187 static unsigned char tmp; 00188 00189 BigDiv(p->p_range, &tmp); 00190 BigMul(0); 00191 BigAdd(tmp + p->p_offset); 00192 } 00193 00194 int KXFace::BigPop(register const Prob *p) 00195 { 00196 static unsigned char tmp; 00197 register int i; 00198 00199 BigDiv(0, &tmp); 00200 i = 0; 00201 while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) 00202 { 00203 p++; 00204 i++; 00205 } 00206 BigMul(p->p_range); 00207 BigAdd(tmp - p->p_offset); 00208 return i; 00209 } 00210 00211 00212 /* Divide B by a storing the result in B and the remainder in the word 00213 * pointer to by r 00214 */ 00215 void KXFace::BigDiv(register unsigned char a, register unsigned char *r) 00216 { 00217 register int i; 00218 register unsigned char *w; 00219 register COMP c, d; 00220 00221 a &= WORDMASK; 00222 if ((a == 1) || (B.b_words == 0)) 00223 { 00224 *r = 0; 00225 return; 00226 } 00227 if (a == 0) /* treat this as a == WORDCARRY */ 00228 { /* and just shift everything right a WORD (unsigned char)*/ 00229 i = --B.b_words; 00230 w = B.b_word; 00231 *r = *w; 00232 while (i--) 00233 { 00234 *w = *(w + 1); 00235 w++; 00236 } 00237 *w = 0; 00238 return; 00239 } 00240 w = B.b_word + (i = B.b_words); 00241 c = 0; 00242 while (i--) 00243 { 00244 c <<= BITSPERWORD; 00245 c += (COMP)*--w; 00246 d = c / (COMP)a; 00247 c = c % (COMP)a; 00248 *w = (unsigned char)(d & WORDMASK); 00249 } 00250 *r = c; 00251 if (B.b_word[B.b_words - 1] == 0) 00252 B.b_words--; 00253 } 00254 00255 /* Multiply a by B storing the result in B 00256 */ 00257 void KXFace::BigMul(register unsigned char a) 00258 { 00259 register int i; 00260 register unsigned char *w; 00261 register COMP c; 00262 00263 a &= WORDMASK; 00264 if ((a == 1) || (B.b_words == 0)) 00265 return; 00266 if (a == 0) /* treat this as a == WORDCARRY */ 00267 { /* and just shift everything left a WORD (unsigned char) */ 00268 if ((i = B.b_words++) >= MAXWORDS - 1) 00269 longjmp(comp_env, ERR_INTERNAL); 00270 w = B.b_word + i; 00271 while (i--) 00272 { 00273 *w = *(w - 1); 00274 w--; 00275 } 00276 *w = 0; 00277 return; 00278 } 00279 i = B.b_words; 00280 w = B.b_word; 00281 c = 0; 00282 while (i--) 00283 { 00284 c += (COMP)*w * (COMP)a; 00285 *(w++) = (unsigned char)(c & WORDMASK); 00286 c >>= BITSPERWORD; 00287 } 00288 if (c) 00289 { 00290 if (B.b_words++ >= MAXWORDS) 00291 longjmp(comp_env, ERR_INTERNAL); 00292 *w = (COMP)(c & WORDMASK); 00293 } 00294 } 00295 00296 /* Add to a to B storing the result in B 00297 */ 00298 void KXFace::BigAdd(unsigned char a) 00299 { 00300 register int i; 00301 register unsigned char *w; 00302 register COMP c; 00303 00304 a &= WORDMASK; 00305 if (a == 0) 00306 return; 00307 i = 0; 00308 w = B.b_word; 00309 c = a; 00310 while ((i < B.b_words) && c) 00311 { 00312 c += (COMP)*w; 00313 *w++ = (unsigned char)(c & WORDMASK); 00314 c >>= BITSPERWORD; 00315 i++; 00316 } 00317 if ((i == B.b_words) && c) 00318 { 00319 if (B.b_words++ >= MAXWORDS) 00320 longjmp(comp_env, ERR_INTERNAL); 00321 *w = (COMP)(c & WORDMASK); 00322 } 00323 } 00324 00325 void KXFace::BigClear() 00326 { 00327 B.b_words = 0; 00328 } 00329 00330 TQCString KXFace::WriteFace() 00331 { 00332 register char *s; 00333 register int i, j, bits, digits, words; 00334 int digsperword = DIGSPERWORD; 00335 int wordsperline = WORDSPERLINE; 00336 TQCString t( "#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n " ); 00337 j = t.length() - 1; 00338 00339 s = F; 00340 bits = digits = words = i = 0; 00341 t.resize( MAX_XFACE_LENGTH ); 00342 digsperword = 2; 00343 wordsperline = 15; 00344 while ( s < F + PIXELS ) 00345 { 00346 if ( ( bits == 0 ) && ( digits == 0 ) ) 00347 { 00348 t[j++] = '0'; 00349 t[j++] = 'x'; 00350 } 00351 if ( *(s++) ) 00352 i = ( i >> 1 ) | 0x8; 00353 else 00354 i >>= 1; 00355 if ( ++bits == BITSPERDIG ) 00356 { 00357 j++; 00358 t[j-( ( digits & 1 ) * 2 )] = *(i + HexDigits); 00359 bits = i = 0; 00360 if ( ++digits == digsperword ) 00361 { 00362 if ( s >= F + PIXELS ) 00363 break; 00364 t[j++] = ','; 00365 digits = 0; 00366 if ( ++words == wordsperline ) 00367 { 00368 t[j++] = '\n'; 00369 t[j++] = ' '; 00370 words = 0; 00371 } 00372 } 00373 } 00374 } 00375 t.resize( j + 1 ); 00376 t += "};\n"; 00377 return t; 00378 } 00379 00380 void KXFace::UnCompAll(char *fbuf) 00381 { 00382 register char *p; 00383 00384 BigClear(); 00385 BigRead(fbuf); 00386 p = F; 00387 while (p < F + PIXELS) 00388 *(p++) = 0; 00389 UnCompress(F, 16, 16, 0); 00390 UnCompress(F + 16, 16, 16, 0); 00391 UnCompress(F + 32, 16, 16, 0); 00392 UnCompress(F + WIDTH * 16, 16, 16, 0); 00393 UnCompress(F + WIDTH * 16 + 16, 16, 16, 0); 00394 UnCompress(F + WIDTH * 16 + 32, 16, 16, 0); 00395 UnCompress(F + WIDTH * 32, 16, 16, 0); 00396 UnCompress(F + WIDTH * 32 + 16, 16, 16, 0); 00397 UnCompress(F + WIDTH * 32 + 32, 16, 16, 0); 00398 } 00399 00400 void KXFace::UnCompress(char *f, int wid, int hei, int lev) 00401 { 00402 switch (BigPop(&levels[lev][0])) 00403 { 00404 case WHITE : 00405 return; 00406 case BLACK : 00407 PopGreys(f, wid, hei); 00408 return; 00409 default : 00410 wid /= 2; 00411 hei /= 2; 00412 lev++; 00413 UnCompress(f, wid, hei, lev); 00414 UnCompress(f + wid, wid, hei, lev); 00415 UnCompress(f + hei * WIDTH, wid, hei, lev); 00416 UnCompress(f + wid + hei * WIDTH, wid, hei, lev); 00417 return; 00418 } 00419 } 00420 00421 void KXFace::BigWrite(register char *fbuf) 00422 { 00423 static unsigned char tmp; 00424 static char buf[DIGITS]; 00425 register char *s; 00426 register int i; 00427 00428 s = buf; 00429 while (B.b_words > 0) 00430 { 00431 BigDiv(NUMPRINTS, &tmp); 00432 *(s++) = tmp + FIRSTPRINT; 00433 } 00434 i = 7; // leave room for the field name on the first line 00435 *(fbuf++) = ' '; 00436 while (s-- > buf) 00437 { 00438 if (i == 0) 00439 *(fbuf++) = ' '; 00440 *(fbuf++) = *s; 00441 if (++i >= MAXLINELEN) 00442 { 00443 *(fbuf++) = '\n'; 00444 i = 0; 00445 } 00446 } 00447 if (i > 0) 00448 *(fbuf++) = '\n'; 00449 *(fbuf++) = '\0'; 00450 } 00451 00452 void KXFace::BigRead(register char *fbuf) 00453 { 00454 register int c; 00455 00456 while (*fbuf != '\0') 00457 { 00458 c = *(fbuf++); 00459 if ((c < FIRSTPRINT) || (c > LASTPRINT)) 00460 continue; 00461 BigMul(NUMPRINTS); 00462 BigAdd((unsigned char)(c - FIRSTPRINT)); 00463 } 00464 } 00465 00466 void KXFace::ReadFace(char *fbuf) 00467 { 00468 register int c, i; 00469 register char *s, *t; 00470 00471 t = s = fbuf; 00472 for(i = strlen(s); i > 0; i--) 00473 { 00474 c = (int)*(s++); 00475 if ((c >= '0') && (c <= '9')) 00476 { 00477 if (t >= fbuf + DIGITS) 00478 { 00479 status = ERR_EXCESS; 00480 break; 00481 } 00482 *(t++) = c - '0'; 00483 } 00484 else if ((c >= 'A') && (c <= 'F')) 00485 { 00486 if (t >= fbuf + DIGITS) 00487 { 00488 status = ERR_EXCESS; 00489 break; 00490 } 00491 *(t++) = c - 'A' + 10; 00492 } 00493 else if ((c >= 'a') && (c <= 'f')) 00494 { 00495 if (t >= fbuf + DIGITS) 00496 { 00497 status = ERR_EXCESS; 00498 break; 00499 } 00500 *(t++) = c - 'a' + 10; 00501 } 00502 else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t-1) == 0)) 00503 t--; 00504 } 00505 if (t < fbuf + DIGITS) 00506 longjmp(comp_env, ERR_INSUFF); 00507 s = fbuf; 00508 t = F; 00509 c = 1 << (BITSPERDIG - 1); 00510 while (t < F + PIXELS) 00511 { 00512 *(t++) = (*s & c) ? 1 : 0; 00513 if ((c >>= 1) == 0) 00514 { 00515 s++; 00516 c = 1 << (BITSPERDIG - 1); 00517 } 00518 } 00519 } 00520 00521 void KXFace::GenFace() 00522 { 00523 static char newp[PIXELS]; 00524 register char *f1; 00525 register char *f2; 00526 register int i; 00527 00528 f1 = newp; 00529 f2 = F; 00530 i = PIXELS; 00531 while (i-- > 0) 00532 *(f1++) = *(f2++); 00533 Gen(newp); 00534 } 00535 00536 void KXFace::UnGenFace() 00537 { 00538 Gen(F); 00539 } 00540 00541 // static 00542 void KXFace::Gen(register char *f) 00543 { 00544 register int m, l, k, j, i, h; 00545 00546 for (j = 0; j < HEIGHT; j++) 00547 { 00548 for (i = 0; i < WIDTH; i++) 00549 { 00550 h = i + j * WIDTH; 00551 k = 0; 00552 for (l = i - 2; l <= i + 2; l++) 00553 for (m = j - 2; m <= j; m++) 00554 { 00555 if ((l >= i) && (m == j)) 00556 continue; 00557 if ((l > 0) && (l <= WIDTH) && (m > 0)) 00558 k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2; 00559 } 00560 switch (i) 00561 { 00562 case 1 : 00563 switch (j) 00564 { 00565 case 1 : GEN(g_22); 00566 case 2 : GEN(g_21); 00567 default : GEN(g_20); 00568 } 00569 break; 00570 case 2 : 00571 switch (j) 00572 { 00573 case 1 : GEN(g_12); 00574 case 2 : GEN(g_11); 00575 default : GEN(g_10); 00576 } 00577 break; 00578 case WIDTH - 1 : 00579 switch (j) 00580 { 00581 case 1 : GEN(g_42); 00582 case 2 : GEN(g_41); 00583 default : GEN(g_40); 00584 } 00585 break; 00586 /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in 00587 because it appears exactly like this in the original compface code. 00588 case WIDTH : 00589 switch (j) 00590 { 00591 case 1 : GEN(g_32); 00592 case 2 : GEN(g_31); 00593 default : GEN(g_30); 00594 } 00595 break; 00596 */ 00597 default : 00598 switch (j) 00599 { 00600 case 1 : GEN(g_02); 00601 case 2 : GEN(g_01); 00602 default : GEN(g_00); 00603 } 00604 break; 00605 } 00606 } 00607 } 00608 } 00609 00610 void KXFace::PopGreys(char *f, int wid, int hei) 00611 { 00612 if (wid > 3) 00613 { 00614 wid /= 2; 00615 hei /= 2; 00616 PopGreys(f, wid, hei); 00617 PopGreys(f + wid, wid, hei); 00618 PopGreys(f + WIDTH * hei, wid, hei); 00619 PopGreys(f + WIDTH * hei + wid, wid, hei); 00620 } 00621 else 00622 { 00623 wid = BigPop(freqs); 00624 if (wid & 1) 00625 *f = 1; 00626 if (wid & 2) 00627 *(f + 1) = 1; 00628 if (wid & 4) 00629 *(f + WIDTH) = 1; 00630 if (wid & 8) 00631 *(f + WIDTH + 1) = 1; 00632 } 00633 } 00634 00635 void KXFace::CompAll(char *fbuf) 00636 { 00637 Compress(F, 16, 16, 0); 00638 Compress(F + 16, 16, 16, 0); 00639 Compress(F + 32, 16, 16, 0); 00640 Compress(F + WIDTH * 16, 16, 16, 0); 00641 Compress(F + WIDTH * 16 + 16, 16, 16, 0); 00642 Compress(F + WIDTH * 16 + 32, 16, 16, 0); 00643 Compress(F + WIDTH * 32, 16, 16, 0); 00644 Compress(F + WIDTH * 32 + 16, 16, 16, 0); 00645 Compress(F + WIDTH * 32 + 32, 16, 16, 0); 00646 BigClear(); 00647 while (NumProbs > 0) 00648 BigPush(ProbBuf[--NumProbs]); 00649 BigWrite(fbuf); 00650 } 00651 00652 void KXFace::Compress(register char *f, register int wid, register int hei, register int lev) 00653 { 00654 if (AllWhite(f, wid, hei)) 00655 { 00656 RevPush(&levels[lev][WHITE]); 00657 return; 00658 } 00659 if (AllBlack(f, wid, hei)) 00660 { 00661 RevPush(&levels[lev][BLACK]); 00662 PushGreys(f, wid, hei); 00663 return; 00664 } 00665 RevPush(&levels[lev][GREY]); 00666 wid /= 2; 00667 hei /= 2; 00668 lev++; 00669 Compress(f, wid, hei, lev); 00670 Compress(f + wid, wid, hei, lev); 00671 Compress(f + hei * WIDTH, wid, hei, lev); 00672 Compress(f + wid + hei * WIDTH, wid, hei, lev); 00673 } 00674 00675 int KXFace::AllWhite(char *f, int wid, int hei) 00676 { 00677 return ((*f == 0) && Same(f, wid, hei)); 00678 } 00679 00680 int KXFace::AllBlack(char *f, int wid, int hei) 00681 { 00682 if (wid > 3) 00683 { 00684 wid /= 2; 00685 hei /= 2; 00686 return (AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) && 00687 AllBlack(f + WIDTH * hei, wid, hei) && 00688 AllBlack(f + WIDTH * hei + wid, wid, hei)); 00689 } 00690 else 00691 return (*f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1)); 00692 } 00693 00694 int KXFace::Same(register char *f, register int wid, register int hei) 00695 { 00696 register char val, *row; 00697 register int x; 00698 00699 val = *f; 00700 while (hei--) 00701 { 00702 row = f; 00703 x = wid; 00704 while (x--) 00705 if (*(row++) != val) 00706 return(0); 00707 f += WIDTH; 00708 } 00709 return 1; 00710 } 00711 00712 void KXFace::PushGreys(char *f, int wid, int hei) 00713 { 00714 if (wid > 3) 00715 { 00716 wid /= 2; 00717 hei /= 2; 00718 PushGreys(f, wid, hei); 00719 PushGreys(f + wid, wid, hei); 00720 PushGreys(f + WIDTH * hei, wid, hei); 00721 PushGreys(f + WIDTH * hei + wid, wid, hei); 00722 } 00723 else 00724 RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) + 00725 8 * *(f + WIDTH + 1)); 00726 } 00727 00728 00729 #include "kxface.moc"