You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kfax/viewfax.cpp

771 lines
18 KiB

/*
KFax -- A G3/G4 Fax Viewer
Copyrigh (C) 1997 Bernd Johannes Wuebben
wuebben@math.cornell.edu
wuebben@kde.org
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 <tqglobal.h>
#include <tqdir.h>
#include <tqfile.h>
#include <tqevent.h>
#include <tqprinter.h>
#include <tqstring.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();
}
TQCString 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(TQFile::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