diff options
Diffstat (limited to 'libkdepim/kxface.cpp')
-rw-r--r-- | libkdepim/kxface.cpp | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/libkdepim/kxface.cpp b/libkdepim/kxface.cpp new file mode 100644 index 000000000..574a0aa9d --- /dev/null +++ b/libkdepim/kxface.cpp @@ -0,0 +1,729 @@ +/* + This file is part of libkdepim. + + Original compface: + Copyright (c) James Ashton - Sydney University - June 1990. + + Additions for KDE: + Copyright (c) 2004 Jakob Schröter <js@camaya.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kxface.h" + +#include <kdebug.h> + +#include <qbuffer.h> +#include <qcstring.h> +#include <qimage.h> +#include <qregexp.h> +#include <qstring.h> +#include <qpainter.h> + +#include <stdlib.h> +#include <string.h> + +#define GEN(g) F[h] ^= G.g[k]; break + +#define BITSPERDIG 4 +#define DIGITS (PIXELS / BITSPERDIG) +#define DIGSPERWORD 4 +#define WORDSPERLINE (WIDTH / DIGSPERWORD / BITSPERDIG) + +/* compressed output uses the full range of printable characters. + * in ascii these are in a contiguous block so we just need to know + * the first and last. The total number of printables is needed too */ +#define FIRSTPRINT '!' +#define LASTPRINT '~' +#define NUMPRINTS (LASTPRINT - FIRSTPRINT + 1) + +/* output line length for compressed data */ +#define MAXLINELEN 78 + +/* Portable, very large unsigned integer arithmetic is needed. + * Implementation uses arrays of WORDs. COMPs must have at least + * twice as many bits as WORDs to handle intermediate results */ +#define COMP unsigned long +#define WORDCARRY (1 << BITSPERWORD) +#define WORDMASK (WORDCARRY - 1) + +#define ERR_OK 0 /* successful completion */ +#define ERR_EXCESS 1 /* completed OK but some input was ignored */ +#define ERR_INSUFF -1 /* insufficient input. Bad face format? */ +#define ERR_INTERNAL -2 /* Arithmetic overflow or buffer overflow */ + +#define BLACK 0 +#define GREY 1 +#define WHITE 2 + +#define MAX_XFACE_LENGTH 2048 + +using namespace KPIM; + +KXFace::KXFace() +{ + NumProbs = 0; +} + +KXFace::~KXFace() +{ +} + +QString KXFace::fromImage( const QImage &image ) +{ + if( image.isNull() ) + return QString::null; + + QImage scaledImg = image.smoothScale( 48, 48 ); + QByteArray ba; + QBuffer buffer( ba ); + buffer.open( IO_WriteOnly ); + scaledImg.save( &buffer, "XBM" ); + QString xbm( ba ); + xbm.remove( 0, xbm.find( "{" ) + 1 ); + xbm.truncate( xbm.find( "}" ) ); + xbm.remove( " " ); + xbm.remove( "," ); + xbm.remove( "0x" ); + xbm.remove( "\n" ); + xbm.truncate( 576 ); + QCString tmp = QCString( xbm.latin1() ); + uint len = tmp.length(); + for( uint i=0; i<len; ++i ) + { + switch( tmp[i] ) + { + case '1': tmp[i] = '8'; break; + case '2': tmp[i] = '4'; break; + case '3': tmp[i] = 'c'; break; + case '4': tmp[i] = '2'; break; + case '5': tmp[i] = 'a'; break; + case '7': tmp[i] = 'e'; break; + case '8': tmp[i] = '1'; break; + case 'A': + case 'a': tmp[i] = '5'; break; + case 'B': + case 'b': tmp[i] = 'd'; break; + case 'C': + case 'c': tmp[i] = '3'; break; + case 'D': + case 'd': tmp[i] = 'b'; break; + case 'E': + case 'e': tmp[i] = '7'; break; + } + if ( i % 2 ) + { + char t = tmp[i]; + tmp[i] = tmp[i-1]; + tmp[i-1] = t; + } + } + tmp.replace( QRegExp( "(\\w{12})" ), "\\1\n" ); + tmp.replace( QRegExp( "(\\w{4})" ), "0x\\1," ); + len = tmp.length(); + char *fbuf = (char *)malloc( len + 1 ); + strncpy( fbuf, (const char *)tmp, len ); + fbuf[len] = '\0'; + if ( !( status = setjmp( comp_env ) ) ) + { + ReadFace( fbuf ); + GenFace(); + CompAll( fbuf ); + } + QString ret( fbuf ); + free( fbuf ); + + return ret; +} + +QImage KXFace::toImage(const QString &xface) +{ + if ( xface.length() > MAX_XFACE_LENGTH ) + return QImage(); + + char *fbuf = (char *)malloc( MAX_XFACE_LENGTH ); + memset( fbuf, '\0', MAX_XFACE_LENGTH ); + strncpy( fbuf, xface.latin1(), xface.length() ); + QCString img; + if ( !( status = setjmp( comp_env ) ) ) + { + UnCompAll( fbuf );/* compress otherwise */ + UnGenFace(); + img = WriteFace(); + } + free( fbuf ); + QImage p; + p.loadFromData( img, "XBM" ); + + return p; +} + +//============================================================================ +// more or less original compface 1.4 source + +void KXFace::RevPush(const Prob *p) +{ + if (NumProbs >= PIXELS * 2 - 1) + longjmp(comp_env, ERR_INTERNAL); + ProbBuf[NumProbs++] = (Prob *) p; +} + +void KXFace::BigPush(Prob *p) +{ + static unsigned char tmp; + + BigDiv(p->p_range, &tmp); + BigMul(0); + BigAdd(tmp + p->p_offset); +} + +int KXFace::BigPop(register const Prob *p) +{ + static unsigned char tmp; + register int i; + + BigDiv(0, &tmp); + i = 0; + while ((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) + { + p++; + i++; + } + BigMul(p->p_range); + BigAdd(tmp - p->p_offset); + return i; +} + + +/* Divide B by a storing the result in B and the remainder in the word + * pointer to by r + */ +void KXFace::BigDiv(register unsigned char a, register unsigned char *r) +{ + register int i; + register unsigned char *w; + register COMP c, d; + + a &= WORDMASK; + if ((a == 1) || (B.b_words == 0)) + { + *r = 0; + return; + } + if (a == 0) /* treat this as a == WORDCARRY */ + { /* and just shift everything right a WORD (unsigned char)*/ + i = --B.b_words; + w = B.b_word; + *r = *w; + while (i--) + { + *w = *(w + 1); + w++; + } + *w = 0; + return; + } + w = B.b_word + (i = B.b_words); + c = 0; + while (i--) + { + c <<= BITSPERWORD; + c += (COMP)*--w; + d = c / (COMP)a; + c = c % (COMP)a; + *w = (unsigned char)(d & WORDMASK); + } + *r = c; + if (B.b_word[B.b_words - 1] == 0) + B.b_words--; +} + +/* Multiply a by B storing the result in B + */ +void KXFace::BigMul(register unsigned char a) +{ + register int i; + register unsigned char *w; + register COMP c; + + a &= WORDMASK; + if ((a == 1) || (B.b_words == 0)) + return; + if (a == 0) /* treat this as a == WORDCARRY */ + { /* and just shift everything left a WORD (unsigned char) */ + if ((i = B.b_words++) >= MAXWORDS - 1) + longjmp(comp_env, ERR_INTERNAL); + w = B.b_word + i; + while (i--) + { + *w = *(w - 1); + w--; + } + *w = 0; + return; + } + i = B.b_words; + w = B.b_word; + c = 0; + while (i--) + { + c += (COMP)*w * (COMP)a; + *(w++) = (unsigned char)(c & WORDMASK); + c >>= BITSPERWORD; + } + if (c) + { + if (B.b_words++ >= MAXWORDS) + longjmp(comp_env, ERR_INTERNAL); + *w = (COMP)(c & WORDMASK); + } +} + +/* Add to a to B storing the result in B + */ +void KXFace::BigAdd(unsigned char a) +{ + register int i; + register unsigned char *w; + register COMP c; + + a &= WORDMASK; + if (a == 0) + return; + i = 0; + w = B.b_word; + c = a; + while ((i < B.b_words) && c) + { + c += (COMP)*w; + *w++ = (unsigned char)(c & WORDMASK); + c >>= BITSPERWORD; + i++; + } + if ((i == B.b_words) && c) + { + if (B.b_words++ >= MAXWORDS) + longjmp(comp_env, ERR_INTERNAL); + *w = (COMP)(c & WORDMASK); + } +} + +void KXFace::BigClear() +{ + B.b_words = 0; +} + +QCString KXFace::WriteFace() +{ + register char *s; + register int i, j, bits, digits, words; + int digsperword = DIGSPERWORD; + int wordsperline = WORDSPERLINE; + QCString t( "#define noname_width 48\n#define noname_height 48\nstatic char noname_bits[] = {\n " ); + j = t.length() - 1; + + s = F; + bits = digits = words = i = 0; + t.resize( MAX_XFACE_LENGTH ); + digsperword = 2; + wordsperline = 15; + while ( s < F + PIXELS ) + { + if ( ( bits == 0 ) && ( digits == 0 ) ) + { + t[j++] = '0'; + t[j++] = 'x'; + } + if ( *(s++) ) + i = ( i >> 1 ) | 0x8; + else + i >>= 1; + if ( ++bits == BITSPERDIG ) + { + j++; + t[j-( ( digits & 1 ) * 2 )] = *(i + HexDigits); + bits = i = 0; + if ( ++digits == digsperword ) + { + if ( s >= F + PIXELS ) + break; + t[j++] = ','; + digits = 0; + if ( ++words == wordsperline ) + { + t[j++] = '\n'; + t[j++] = ' '; + words = 0; + } + } + } + } + t.resize( j + 1 ); + t += "};\n"; + return t; +} + +void KXFace::UnCompAll(char *fbuf) +{ + register char *p; + + BigClear(); + BigRead(fbuf); + p = F; + while (p < F + PIXELS) + *(p++) = 0; + UnCompress(F, 16, 16, 0); + UnCompress(F + 16, 16, 16, 0); + UnCompress(F + 32, 16, 16, 0); + UnCompress(F + WIDTH * 16, 16, 16, 0); + UnCompress(F + WIDTH * 16 + 16, 16, 16, 0); + UnCompress(F + WIDTH * 16 + 32, 16, 16, 0); + UnCompress(F + WIDTH * 32, 16, 16, 0); + UnCompress(F + WIDTH * 32 + 16, 16, 16, 0); + UnCompress(F + WIDTH * 32 + 32, 16, 16, 0); +} + +void KXFace::UnCompress(char *f, int wid, int hei, int lev) +{ + switch (BigPop(&levels[lev][0])) + { + case WHITE : + return; + case BLACK : + PopGreys(f, wid, hei); + return; + default : + wid /= 2; + hei /= 2; + lev++; + UnCompress(f, wid, hei, lev); + UnCompress(f + wid, wid, hei, lev); + UnCompress(f + hei * WIDTH, wid, hei, lev); + UnCompress(f + wid + hei * WIDTH, wid, hei, lev); + return; + } +} + +void KXFace::BigWrite(register char *fbuf) +{ + static unsigned char tmp; + static char buf[DIGITS]; + register char *s; + register int i; + + s = buf; + while (B.b_words > 0) + { + BigDiv(NUMPRINTS, &tmp); + *(s++) = tmp + FIRSTPRINT; + } + i = 7; // leave room for the field name on the first line + *(fbuf++) = ' '; + while (s-- > buf) + { + if (i == 0) + *(fbuf++) = ' '; + *(fbuf++) = *s; + if (++i >= MAXLINELEN) + { + *(fbuf++) = '\n'; + i = 0; + } + } + if (i > 0) + *(fbuf++) = '\n'; + *(fbuf++) = '\0'; +} + +void KXFace::BigRead(register char *fbuf) +{ + register int c; + + while (*fbuf != '\0') + { + c = *(fbuf++); + if ((c < FIRSTPRINT) || (c > LASTPRINT)) + continue; + BigMul(NUMPRINTS); + BigAdd((unsigned char)(c - FIRSTPRINT)); + } +} + +void KXFace::ReadFace(char *fbuf) +{ + register int c, i; + register char *s, *t; + + t = s = fbuf; + for(i = strlen(s); i > 0; i--) + { + c = (int)*(s++); + if ((c >= '0') && (c <= '9')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - '0'; + } + else if ((c >= 'A') && (c <= 'F')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - 'A' + 10; + } + else if ((c >= 'a') && (c <= 'f')) + { + if (t >= fbuf + DIGITS) + { + status = ERR_EXCESS; + break; + } + *(t++) = c - 'a' + 10; + } + else if (((c == 'x') || (c == 'X')) && (t > fbuf) && (*(t-1) == 0)) + t--; + } + if (t < fbuf + DIGITS) + longjmp(comp_env, ERR_INSUFF); + s = fbuf; + t = F; + c = 1 << (BITSPERDIG - 1); + while (t < F + PIXELS) + { + *(t++) = (*s & c) ? 1 : 0; + if ((c >>= 1) == 0) + { + s++; + c = 1 << (BITSPERDIG - 1); + } + } +} + +void KXFace::GenFace() +{ + static char newp[PIXELS]; + register char *f1; + register char *f2; + register int i; + + f1 = newp; + f2 = F; + i = PIXELS; + while (i-- > 0) + *(f1++) = *(f2++); + Gen(newp); +} + +void KXFace::UnGenFace() +{ + Gen(F); +} + +// static +void KXFace::Gen(register char *f) +{ + register int m, l, k, j, i, h; + + for (j = 0; j < HEIGHT; j++) + { + for (i = 0; i < WIDTH; i++) + { + h = i + j * WIDTH; + k = 0; + for (l = i - 2; l <= i + 2; l++) + for (m = j - 2; m <= j; m++) + { + if ((l >= i) && (m == j)) + continue; + if ((l > 0) && (l <= WIDTH) && (m > 0)) + k = *(f + l + m * WIDTH) ? k * 2 + 1 : k * 2; + } + switch (i) + { + case 1 : + switch (j) + { + case 1 : GEN(g_22); + case 2 : GEN(g_21); + default : GEN(g_20); + } + break; + case 2 : + switch (j) + { + case 1 : GEN(g_12); + case 2 : GEN(g_11); + default : GEN(g_10); + } + break; + case WIDTH - 1 : + switch (j) + { + case 1 : GEN(g_42); + case 2 : GEN(g_41); + default : GEN(g_40); + } + break; + /* i runs from 0 to WIDTH-1, so case can never occur. I leave the code in + because it appears exactly like this in the original compface code. + case WIDTH : + switch (j) + { + case 1 : GEN(g_32); + case 2 : GEN(g_31); + default : GEN(g_30); + } + break; + */ + default : + switch (j) + { + case 1 : GEN(g_02); + case 2 : GEN(g_01); + default : GEN(g_00); + } + break; + } + } + } +} + +void KXFace::PopGreys(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + PopGreys(f, wid, hei); + PopGreys(f + wid, wid, hei); + PopGreys(f + WIDTH * hei, wid, hei); + PopGreys(f + WIDTH * hei + wid, wid, hei); + } + else + { + wid = BigPop(freqs); + if (wid & 1) + *f = 1; + if (wid & 2) + *(f + 1) = 1; + if (wid & 4) + *(f + WIDTH) = 1; + if (wid & 8) + *(f + WIDTH + 1) = 1; + } +} + +void KXFace::CompAll(char *fbuf) +{ + Compress(F, 16, 16, 0); + Compress(F + 16, 16, 16, 0); + Compress(F + 32, 16, 16, 0); + Compress(F + WIDTH * 16, 16, 16, 0); + Compress(F + WIDTH * 16 + 16, 16, 16, 0); + Compress(F + WIDTH * 16 + 32, 16, 16, 0); + Compress(F + WIDTH * 32, 16, 16, 0); + Compress(F + WIDTH * 32 + 16, 16, 16, 0); + Compress(F + WIDTH * 32 + 32, 16, 16, 0); + BigClear(); + while (NumProbs > 0) + BigPush(ProbBuf[--NumProbs]); + BigWrite(fbuf); +} + +void KXFace::Compress(register char *f, register int wid, register int hei, register int lev) +{ + if (AllWhite(f, wid, hei)) + { + RevPush(&levels[lev][WHITE]); + return; + } + if (AllBlack(f, wid, hei)) + { + RevPush(&levels[lev][BLACK]); + PushGreys(f, wid, hei); + return; + } + RevPush(&levels[lev][GREY]); + wid /= 2; + hei /= 2; + lev++; + Compress(f, wid, hei, lev); + Compress(f + wid, wid, hei, lev); + Compress(f + hei * WIDTH, wid, hei, lev); + Compress(f + wid + hei * WIDTH, wid, hei, lev); +} + +int KXFace::AllWhite(char *f, int wid, int hei) +{ + return ((*f == 0) && Same(f, wid, hei)); +} + +int KXFace::AllBlack(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + return (AllBlack(f, wid, hei) && AllBlack(f + wid, wid, hei) && + AllBlack(f + WIDTH * hei, wid, hei) && + AllBlack(f + WIDTH * hei + wid, wid, hei)); + } + else + return (*f || *(f + 1) || *(f + WIDTH) || *(f + WIDTH + 1)); +} + +int KXFace::Same(register char *f, register int wid, register int hei) +{ + register char val, *row; + register int x; + + val = *f; + while (hei--) + { + row = f; + x = wid; + while (x--) + if (*(row++) != val) + return(0); + f += WIDTH; + } + return 1; +} + +void KXFace::PushGreys(char *f, int wid, int hei) +{ + if (wid > 3) + { + wid /= 2; + hei /= 2; + PushGreys(f, wid, hei); + PushGreys(f + wid, wid, hei); + PushGreys(f + WIDTH * hei, wid, hei); + PushGreys(f + WIDTH * hei + wid, wid, hei); + } + else + RevPush(freqs + *f + 2 * *(f + 1) + 4 * *(f + WIDTH) + + 8 * *(f + WIDTH + 1)); +} + + +#include "kxface.moc" |