diff options
Diffstat (limited to 'kfax/viewfax.cpp')
-rw-r--r-- | kfax/viewfax.cpp | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/kfax/viewfax.cpp b/kfax/viewfax.cpp new file mode 100644 index 00000000..fe1e4246 --- /dev/null +++ b/kfax/viewfax.cpp @@ -0,0 +1,770 @@ +/* + + KFax -- A G3/G4 Fax Viewer + + Copyrigh (C) 1997 Bernd Johannes Wuebben + + Based on: + + viewfax - g3/g4 fax processing software. + Copyright (C) 1990, 1995 Frank D. Cringle. + + This file is part of viewfax - g3/g4 fax processing software. + + viewfax is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> +#include <unistd.h> + +#include <qglobal.h> +#include <qdir.h> +#include <qfile.h> +#include <qevent.h> +#include <qprinter.h> +#include <qstring.h> + +#include <kcmdlineargs.h> +#include <klocale.h> + +#include "kfax.h" +#include "faxexpand.h" +#include "version.h" +#include "viewfax.h" + +/* NewImage() needs to fiddle with the Display structure */ +#define XLIB_ILLEGAL_ACCESS +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +#include <X11/cursorfont.h> + + +#define VIEWFAXVERSION "2.3" + + +/* If moving the image around with the middle mouse button is jerky or + slow, try defining USE_MOTIONHINT. It may help (it may also make + things worse - it depends on the server implementation) */ +#undef USE_MOTIONHINT + + + +/* access the 'extra' field in a pagenode */ + +#define Pimage(p) ((XImage *)(p)->extra) + + +/* forward declarations */ + +XImage *FlipImage(XImage *xi); +XImage *MirrorImage(XImage *xi); +XImage *NewImage(int w, int h, char *data, int bit_order); +XImage *RotImage(XImage *Image); +XImage *ZoomImage(XImage *Big); + +void FreeImage(XImage *Image); + +static int release(int quit); + + +/* X variables */ + +extern Cursor WorkCursor; +extern Cursor ReadyCursor; +extern Cursor MoveCursor; +extern Cursor LRCursor; +extern Cursor UDCursor; + +extern bool have_no_fax; +extern Display* qtdisplay ; +extern Window qtwin; +extern Window Win; +extern int qwindow_width; +extern int qwindow_height; + +struct pagenode *firstpage, *lastpage, *thispage, *helppage, *auxpage; +struct pagenode defaultpage; + +Display *Disp; + +int Default_Screen; +int verbose = 0; + +int abell = 1; /* audio bell */ +int vbell = 0; /* visual bell */ +bool have_cmd_opt = FALSE; + +size_t Memused = 0; /* image memory usage */ +static size_t Memlimit = 8*1024*1024; /* try not to exceed */ + +#undef min +#undef max +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#endif + +/* OK, OK - this is a dreadful hack. But it adequately distinguishes + modern big- and little- endian hosts. We only need this to set the + byte order in XImage structures */ + +static union { t32bits i; unsigned char b[4]; } bo; +#define ByteOrder bo.b[3] + +static char Banner[] = +"KFax version " KFAXVERSION "\tCopyright (C) 1997, Bernd Johannes Wuebben\n"; + +/*"KFax is based on:\n" +"viewfax " VERSION ":\t\tCopyright (c) 1990, 1995 Frank D. Cringle.\n" +"libtiff v 3.4beta:\tCopyright (c) 1988 - 1955 Sam Leffler\n" +" \tCopyright (c) 1991 - 1995 Silicon Graphics, Inc.\n\n" +"KFax comes with ABSOLUTELY NO WARRANTY; for details see the\n" +"file \"COPYING\" in the distribution directory.\n";*/ + +XEvent Event; +XImage *Image, *Images[MAXZOOM]; +XSizeHints size_hints; + +Time Lasttime = 0; + +struct pagenode *viewpage = 0; + +int viewfaxmain() +{ + int banner = 0; + int have_height = 0; + + bo.i = 1; + defaultpage.vres = -1; + have_no_fax = TRUE; + + /* TODO Do I need to know this: */ + defaultpage.expander = g31expand; + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + if (args->isSet("height")) + { + have_height = 1; + defaultpage.height = args->getOption("height").toInt(); + } + + if (args->isSet("2")) + { + defaultpage.expander = g32expand; + if(!have_height) + defaultpage.height = 2339; + } + + if (args->isSet("4")) + { + defaultpage.expander = g4expand; + if(!have_height) + defaultpage.height = 2155; + } + + if (args->isSet("invert")) + { + defaultpage.inverse = 1; + } + + if (args->isSet("landscape")) + { + defaultpage.orient |= TURN_L; + } + + if (args->isSet("fine")) + { + defaultpage.vres = 1; + } + + if (!args->isSet("rmal")) // "normal" is interpreted as "no"+"rmal" :-) + { + defaultpage.vres = 0; + } + + if (args->isSet("reverse")) + { + defaultpage.lsbfirst = 1; + } + + if (args->isSet("upsidedown")) + { + defaultpage.orient |= TURN_U; + } + + if (args->isSet("width")) + { + defaultpage.width = args->getOption("width").toInt(); + } + + QCString mem = args->getOption("mem"); + Memlimit = atoi(mem.data()); + switch(mem[mem.length()-1]) { + case 'M': + case 'm': + Memlimit *= 1024; + case 'K': + case 'k': + Memlimit *= 1024; + } + + if (defaultpage.expander == g4expand && defaultpage.height == 0) { + KCmdLineArgs::usage("--height value is required to interpret raw g4 faxes\n"); + } + + firstpage = lastpage = thispage = helppage = auxpage = 0; + + for (int i = 0; i < args->count(); i++){ + loadfile(QFile::decodeName(args->arg(i))); + } + args->clear(); + + if (banner ) { + fputs(Banner, stderr); + exit(1); + } + + have_no_fax = (firstpage == 0); + + Disp = qtdisplay; + Default_Screen = XDefaultScreen(qtdisplay); + + return 1; +} + + +/* Change orientation of all following pages */ +void TurnFollowing(int How, struct pagenode *pn) +{ + while (pn) { + if (Pimage(pn)) { + FreeImage(Pimage(pn)); + pn->extra = 0; + } + pn->orient ^= How; + pn = pn->next; + } +} + +static void +drawline(pixnum *run, int LineNum, struct pagenode *pn) +{ + t32bits *p, *p1; /* p - current line, p1 - low-res duplicate */ + pixnum *r; /* pointer to run-lengths */ + t32bits pix; /* current pixel value */ + t32bits acc; /* pixel accumulator */ + int nacc; /* number of valid bits in acc */ + int tot; /* total pixels in line */ + int n; + + LineNum += pn->stripnum * pn->rowsperstrip; + if (LineNum >= pn->height) { + if (verbose && LineNum == pn->height) + fputs("Height exceeded\n", stderr); + return; + } + + p = (t32bits *) (Pimage(pn)->data + LineNum*(2-pn->vres)*Pimage(pn)->bytes_per_line); + p1 =(t32bits *)( pn->vres ? 0 : p + Pimage(pn)->bytes_per_line/sizeof(*p)); + r = run; + acc = 0; + nacc = 0; + pix = pn->inverse ? ~0 : 0; + tot = 0; + while (tot < pn->width) { + n = *r++; + tot += n; + /* Watch out for buffer overruns, e.g. when n == 65535. */ + if (tot > pn->width) + break; + if (pix) + acc |= (~(t32bits)0 >> nacc); + else if (nacc) + acc &= (~(t32bits)0 << (32 - nacc)); + else + acc = 0; + if (nacc + n < 32) { + nacc += n; + pix = ~pix; + continue; + } + *p++ = acc; + if (p1) + *p1++ = acc; + n -= 32 - nacc; + while (n >= 32) { + n -= 32; + *p++ = pix; + if (p1) + *p1++ = pix; + } + acc = pix; + nacc = n; + pix = ~pix; + } + if (nacc) { + *p++ = acc; + if (p1) + *p1++ = acc; + } +} + +static int +GetPartImage(struct pagenode *pn, int n) +{ + unsigned char *Data = getstrip(pn, n); + + if (Data == 0) + return 3; + pn->stripnum = n; + (*pn->expander)(pn, drawline); + free(Data); + return 1; +} + +int GetImage(struct pagenode *pn){ + int i; + + if (pn->strips == 0) { + + /*printf("RAW fax file\n");*/ + + /* raw file; maybe we don't have the height yet */ + unsigned char *Data = getstrip(pn, 0); + if (Data == 0){ + + return 0; + } + pn->extra = NewImage(pn->width, pn->vres ? + pn->height : 2*pn->height, 0, 1); + +//printf("height = %d\n",pn->height); +//printf("setting height to %d\n", pn->vres ? pn->height : 2*pn->height); + + if(pn->extra == 0) + return 0; + + (*pn->expander)(pn, drawline); + } + else { + /* multi-strip tiff */ + /*printf("MULTI STRIP TIFF fax file\n");*/ + + pn->extra = NewImage(pn->width, pn->vres ? + pn->height : 2*pn->height, 0, 1); + + if(pn->extra == 0) + return 0; + pn->stripnum = 0; + for (i = 0; i < pn->nstrips; i++){ + + int k =GetPartImage(pn, i); + + if ( k == 3 ){ + FreeImage(Pimage(pn)); + return k; + } + + } + } + if (pn->orient & TURN_U) + pn->extra = FlipImage(Pimage(pn)); + if (pn->orient & TURN_M) + pn->extra = MirrorImage(Pimage(pn)); + if (pn->orient & TURN_L) + pn->extra = RotImage(Pimage(pn)); + if (verbose) printf("\tmemused = %d\n", Memused); + +/* +if(pn->extra) + printf("pn->extra !=0 %s\n",pn->name); +else + printf("pn->extra ==0 %s\n",pn->name); + */ + + return 1; +} + + + +/* run this region through perl to generate the zoom table: +$lim = 1; +@c = ("0", "1", "1", "2"); +print "static unsigned char Z[] = {\n"; +for ($i = 0; $i < 16; $i++) { + for ($j = 0; $j < 16; $j++) { + $b1 = ($c[$j&3]+$c[$i&3]) > $lim; + $b2 = ($c[($j>>2)&3]+$c[($i>>2)&3]) > $lim; + printf " %X,", ($b2 << 1) | $b1; + } + print "\n"; +} +print "};\n"; +*/ + +static unsigned char Z[] = { + 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, + 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, + 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, + 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, + 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +#define nib(n,w) (((w)>>((n)<<2))&15) +#define zak(a,b) Z[(a<<4)|b] + +/* 2 -> 1 zoom, 4 pixels -> 1 pixel + if #pixels <= $lim (see above), new pixel is white, + else black. +*/ + +XImage *ZoomImage(XImage *Big){ + + XImage *Small; + int w, h; + int i, j; + + XDefineCursor(Disp, Win, WorkCursor); + XFlush(Disp); + w = (Big->width+1) / 2; + h = (Big->height+1) / 2; + Small = NewImage(w, h, 0, Big->bitmap_bit_order); + if(Small == 0) + return 0; + + Small->xoffset = (Big->xoffset+1)/2; + for (i = 0; i < Big->height; i += 2) { + t32bits *pb0 = (t32bits *) (Big->data + i * Big->bytes_per_line); + t32bits *pb1 = pb0 + ((i == Big->height-1) ? 0 : Big->bytes_per_line/4); + t32bits *ps = (t32bits *) (Small->data + i * Small->bytes_per_line / 2); + for (j = 0; j < Big->bytes_per_line/8; j++) { + t32bits r1, r2; + t32bits t0 = *pb0++; + t32bits t1 = *pb1++; + r1 = (zak(nib(7,t0),nib(7,t1))<<14) | + (zak(nib(6,t0),nib(6,t1))<<12) | + (zak(nib(5,t0),nib(5,t1))<<10) | + (zak(nib(4,t0),nib(4,t1))<<8) | + (zak(nib(3,t0),nib(3,t1))<<6) | + (zak(nib(2,t0),nib(2,t1))<<4) | + (zak(nib(1,t0),nib(1,t1))<<2) | + (zak(nib(0,t0),nib(0,t1))); + t0 = *pb0++; + t1 = *pb1++; + r2 = (zak(nib(7,t0),nib(7,t1))<<14) | + (zak(nib(6,t0),nib(6,t1))<<12) | + (zak(nib(5,t0),nib(5,t1))<<10) | + (zak(nib(4,t0),nib(4,t1))<<8) | + (zak(nib(3,t0),nib(3,t1))<<6) | + (zak(nib(2,t0),nib(2,t1))<<4) | + (zak(nib(1,t0),nib(1,t1))<<2) | + (zak(nib(0,t0),nib(0,t1))); + *ps++ = (Big->bitmap_bit_order) ? + (r1<<16)|r2 : (r2<<16)|r1; + } + for ( ; j < Small->bytes_per_line/4; j++) { + t32bits r1; + t32bits t0 = *pb0++; + t32bits t1 = *pb1++; + r1 = (zak(nib(7,t0),nib(7,t1))<<14) | + (zak(nib(6,t0),nib(6,t1))<<12) | + (zak(nib(5,t0),nib(5,t1))<<10) | + (zak(nib(4,t0),nib(4,t1))<<8) | + (zak(nib(3,t0),nib(3,t1))<<6) | + (zak(nib(2,t0),nib(2,t1))<<4) | + (zak(nib(1,t0),nib(1,t1))<<2) | + (zak(nib(0,t0),nib(0,t1))); + *ps++ = (Big->bitmap_bit_order) ? + (r1<<16) : r1; + } + } + XDefineCursor(Disp, Win, ReadyCursor); + return Small; +} + +XImage *FlipImage(XImage *Image){ + + XImage *New = NewImage(Image->width, Image->height, + Image->data, !Image->bitmap_bit_order); + if(New == 0) + return 0; + + t32bits *p1 = (t32bits *) Image->data; + t32bits *p2 = (t32bits *) (Image->data + Image->height * + Image->bytes_per_line - 4); + + /* the first shall be last ... */ + while (p1 < p2) { + t32bits t = *p1; + *p1++ = *p2; + *p2-- = t; + } + + /* let Xlib twiddle the bits */ + New->xoffset = 32 - (Image->width & 31) - Image->xoffset; + New->xoffset &= 31; + + Image->data = 0; + FreeImage(Image); + return New; +} + +XImage *MirrorImage(XImage *Image){ + + int i; + XImage *New = NewImage(Image->width, Image->height, + Image->data, !Image->bitmap_bit_order); + if(New == 0) + return 0; + + /* reverse order of 32-bit words in each line */ + for (i = 0; i < Image->height; i++) { + t32bits *p1 = (t32bits *) (Image->data + Image->bytes_per_line * i); + t32bits *p2 = p1 + Image->bytes_per_line/4 - 1; + while (p1 < p2) { + t32bits t = *p1; + *p1++ = *p2; + *p2-- = t; + } + } + + /* let Xlib twiddle the bits */ + New->xoffset = 32 - (Image->width & 31) - Image->xoffset; + New->xoffset &= 31; + + Image->data = 0; + FreeImage(Image); + return New; +} + +XImage *RotImage(XImage *Image){ + + XImage *New; + int w = Image->height; + int h = Image->width; + int i, j, k, shift; + int order = Image->bitmap_bit_order; + int offs = h+Image->xoffset-1; + + New = NewImage(w, h, 0, 1); + if (New == 0) + return 0; + + k = (32 - Image->xoffset) & 3; + for (i = h - 1; i && k; i--, k--) { + t32bits *sp = (t32bits *) Image->data + (offs-i)/32; + t32bits *dp = (t32bits *) (New->data+i*New->bytes_per_line); + t32bits d0 =0; + shift = (offs-i)&31; + if (order) shift = 31-shift; + for (j = 0; j < w; j++) { + t32bits t = *sp; + sp += Image->bytes_per_line/4; + d0 |= ((t >> shift) & 1); + if ((j & 31) == 31) + *dp++ = d0; + d0 <<= 1;; + } + if (j & 31) + *dp++ = d0<<(31-j); + } + for ( ; i >= 3; i-=4) { + t32bits *sp = (t32bits *) Image->data + (offs-i)/32; + t32bits *dp0 = (t32bits *) (New->data+i*New->bytes_per_line); + t32bits *dp1 = dp0 - New->bytes_per_line/4; + t32bits *dp2 = dp1 - New->bytes_per_line/4; + t32bits *dp3 = dp2 - New->bytes_per_line/4; + t32bits d0=0 , d1=0, d2 =0, d3 =0; + shift = (offs-i)&31; + if (order) shift = 28-shift; + for (j = 0; j < w; j++) { + t32bits t = *sp >> shift; + sp += Image->bytes_per_line/4; + d0 |= t & 1; t >>= 1; + d1 |= t & 1; t >>= 1; + d2 |= t & 1; t >>= 1; + d3 |= t & 1; t >>= 1; + if ((j & 31) == 31) { + if (order) { + *dp0++ = d3; + *dp1++ = d2; + *dp2++ = d1; + *dp3++ = d0; + } + else { + *dp0++ = d0; + *dp1++ = d1; + *dp2++ = d2; + *dp3++ = d3; + } + } + d0 <<= 1; d1 <<= 1; d2 <<= 1; d3 <<= 1; + } + if (j & 31) { + if (order) { + *dp0++ = d3<<(31-j); + *dp1++ = d2<<(31-j); + *dp2++ = d1<<(31-j); + *dp3++ = d0<<(31-j); + } + else { + *dp0++ = d0<<(31-j); + *dp1++ = d1<<(31-j); + *dp2++ = d2<<(31-j); + *dp3++ = d3<<(31-j); + } + } + } + for (; i >= 0; i--) { + t32bits *sp = (t32bits *) Image->data + (offs-i)/32; + t32bits *dp = (t32bits *) (New->data+i*New->bytes_per_line); + t32bits d0=0; + shift = (offs-i)&31; + if (order) shift = 31-shift; + for (j = 0; j < w; j++) { + t32bits t = *sp; + sp += Image->bytes_per_line/4; + d0 |= ((t >> shift) & 1); + if ((j & 31) == 31) + *dp++ = d0; + d0 <<= 1;; + } + if (j & 31) + *dp++ = d0<<(31-j); + } + FreeImage(Image); + return New; +} + +/* release some non-essential memory or abort */ +#define Try(n) \ + if (n && n != thispage && n->extra) { \ + FreeImage((XImage*)n->extra); \ + n->extra = 0; \ + return 1; \ + } + +static int +release(int quit) +{ + (void) quit; + + struct pagenode *pn; + + if (thispage) { + /* first consider "uninteresting" pages */ + for (pn = firstpage->next; pn; pn = pn->next) + if (pn->extra && pn != thispage && pn != thispage->prev && + pn != thispage->next && pn != lastpage) { + FreeImage(Pimage(pn)); + pn->extra = 0; + return 1; + } + Try(lastpage); + Try(firstpage); + Try(thispage->prev); + Try(thispage->next); + } + + return 0; + +} + +XImage *NewImage(int w, int h, char *data, int bit_order){ + + XImage *newimage; + /* This idea is taken from xwud/xpr. Use a fake display with the + desired bit/byte order to get the image routines initialised + correctly */ + Display fake; + + fake = *Disp; + if (data == 0) + data = xmalloc(((w + 31) & ~31) * h / 8); + fake.byte_order = ByteOrder; + fake.bitmap_unit = 32; + fake.bitmap_bit_order = bit_order; + + int returncode = -1; + while ((newimage = XCreateImage(&fake, DefaultVisual(Disp, Default_Screen), + 1, XYBitmap, 0, data, w, h, 32, 0)) == 0 ){ + + returncode = release(1); + if (returncode == 0) + break; + } + + if (returncode == 0){ + kfaxerror("Sorry","Can not allocate Memory for a new Fax Image\n"); + return 0; + } + + Memused += newimage->bytes_per_line * newimage->height; + /*printf("allocating %d bytes for %ld\n", + newimage->bytes_per_line * newimage->height, + newimage);*/ + + + return newimage; +} + +void FreeImage(XImage *Image){ + + if (Image->data){ + Memused -= Image->bytes_per_line * Image->height; +/*printf("deallocating %d bytes for %ld\n", + Image->bytes_per_line * Image->height, + Image);*/ + } + XDestroyImage(Image); +} + +#ifndef xmalloc +char * +xmalloc(unsigned int size) +{ + char *p; + + while (Memused + size > Memlimit && release(0)) + ; + while ((p = (char*) malloc(size)) == 0) + (void) release(1); + return p; +} +#endif |