#include <aes.h>
#include <vdi.h>
#include <osbind.h>
#include <stdlib.h>
#include <string.h>
#include <scancode.h>
#include <errno.h>

typedef struct view
{
  int	handle, planes,
	lbc,			/* left-border-count fr Fenster.	*/
	tlc,			/* top-line-count fr Fenster.		*/
	bc,			/* border-count fr Fenster.		*/
	lc,			/* line-count fr Fenster.		*/
	xfac, yfac,		/* Scroll- und Rastereinheiten.		*/
	x, y, w, h,		/* Arbeitsbereich des Fensters.		*/
	hslide, vslide,		/* Puffer fr Sliderpositionen.		*/
	(*key)( struct view *tv, int code, int ks );
  void	(*draw)( struct view *tv, int *clip ),
	(*free)( struct view *tv ),
	(*sclick)( struct view *tv, int mx, int my, int flag );
  char	path[128];		/* Datei-Pfad (Fenstertitel).		*/
  struct view *next;		/* Zeiger auf nchste Fensterstruktur.	*/
  struct view *prev;		/* Zeiger auf vorige Fensterstruktur.	*/
  BASPAG *actpd;		/* Zeiger auf Prozess-Descriptor.	*/

  MFDB	mfdb;
  int   width, height, x_flag, scale;
	rgb_list[8];		/* Farbpalette.				*/
}
VIEW;

#include "1stview.h"
#include "util.h"

#ifdef __TOS__
#define X_MAGIC  'XIMG'
#define FM_MAGIC 'FORM'
#define FM_IMAGE 'ILBM'
#define BM_MAGIC 'BMHD'
#define CM_MAGIC 'CMAP'
#define BO_MAGIC 'BODY'
#else
#define X_MAGIC  'X'+((long)'I'<<8)+((long)'M'<<16)+((long)'G'<<24)
#define FM_MAGIC 'F'+((long)'O'<<8)+((long)'R'<<16)+((long)'M'<<24)
#define FM_IMAGE 'I'+((long)'L'<<8)+((long)'B'<<16)+((long)'M'<<24)
#define BM_MAGIC 'B'+((long)'M'<<8)+((long)'H'<<16)+((long)'D'<<24)
#define CM_MAGIC 'C'+((long)'M'<<8)+((long)'A'<<16)+((long)'P'<<24)
#define BO_MAGIC 'B'+((long)'O'<<8)+((long)'D'<<16)+((long)'Y'<<24)
#endif

typedef struct
{
  int version, headlen, planes, pat_run,
      pix_width, pix_height, sl_width, sl_height;
}
IMG_HEADER;

typedef struct
{
  long magic, length;
}
CHUNK_HEADER;

typedef struct
{
  long fm_magic, fm_length, fm_type;
}
FORMCHUNK;

typedef struct
{
  long bm_magic, bm_length;
  int  bm_width, bm_height, bm_x, bm_y;
  char bm_nPlanes, bm_Masking, bm_Compression, bm_pad1;
  int  bm_transparentColor;
  char bm_xAspect, bm_yAspect;
  int  bm_pageWidth, bm_pageHeight;
}
BMHDCHUNK;

static long four[256];
static int  doub[256], maxconv;
static char half[512];
static unsigned char Pli2Vdi[256], Vdi2Pli[256];
static char string2[] = { 0, 0, 0, 0 },
	    string3[] = "XXXXX *XXXXX Pixel",
	    string4[] = "---- XX Ebenen ----",
	    string5[] = " Sichern (XIMG) ^X",
	    ostring[] = "  Original-Pal. ^E",
	    sstring[] = "  Standard-Pal. ^S",
	    dithermatrix[] = {
				17, 61, 27, 51, 18, 63, 25, 49,
				41,  5, 38, 14, 42,  7, 37, 13,
				31, 55, 20, 56, 28, 52, 22, 59,
				34, 10, 44,  0, 32,  8, 46,  3,
				19, 62, 24, 48, 16, 60, 26, 50,
				43,  6, 36, 12, 40,  4, 39, 15,
				29, 53, 23, 58, 30, 54, 21, 57,
				33,  9, 47,  2, 35, 11, 45,  1
			     };
static int defcols[][3] = {
				1000, 1000, 1000,
				1000,    0,    0,
				   0, 1000,    0,
				1000, 1000,    0, 
				   0,    0, 1000,
				1000,    0, 1000,
				   0, 1000, 1000,
				 666,  666,  666,
				 333,  333,  333,
				 666,    0,    0,
				   0,  666,    0,
				 666,  666,    0,
				   0,    0,  666,
				 666,    0,  666,
				   0,  666,  666,
				   0,    0,    0
			  };
#pragma warn -rpt
static OBJECT popup[] =
{
  0,  1,  9, G_BOX,    NONE,   SHADOWED, 0xFF1100L,		0, 0, 19,7,
  5, -1, -1, G_STRING, NONE,   DISABLED, string3,		0, 0, 19,1,
  3, -1, -1, G_BUTTON, NONE,   NORMAL,   "-- ^\004",		0, 1,  7,1,
  4, -1, -1, G_BUTTON, NONE,   DISABLED, string2,		7, 1,  5,1,
  6, -1, -1, G_BUTTON, NONE,   NORMAL,   "++ ^\003",		12,1,  7,1,
  2, -1, -1, G_STRING, NONE,   NORMAL,   ostring,		0, 2, 19,1,
  7, -1, -1, G_STRING, NONE,   DISABLED, string4,		0, 3, 19,1,
  8, -1, -1, G_STRING, NONE,   DISABLED, "  Transparent   ^K",	0, 4, 19,1,
  9, -1, -1, G_STRING, NONE,   DISABLED, "--- Farbpalette ---", 0, 5, 19,1,
  0, -1, -1, G_STRING, LASTOB, NORMAL,   string5,		0, 6, 19,1
};
#pragma warn +rpt

static long gray( int *rgb )
{
  return (598L * rgb[0] + 1174L * rgb[1] + 228L * rgb[2] + 15625) / 31250;
}

static int get_pix( void )
{
  int pel[2];

  *(long *)pel = 0; v_pmarker( handle, 1, pel );
  v_get_pixel( handle, 0, 0, pel, pel + 1 );
  return *pel;
}

static void get_true( int *rgb, void *buf )
{
  static int pxy[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  static MFDB screen = { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
	      check  = { 0, 0, 1, 0, 0, 0, 0, 0, 0 };

  vs_color( handle, 1, rgb ); v_pmarker( handle, 1, pxy );
  check.fd_addr = buf;
  check.fd_nplanes = check.fd_w = nplanes;
  check.fd_wdwidth = (check.fd_w + 15) >> 4;
  vro_cpyfm( handle, S_ONLY, pxy, &screen, &check );
}

static int pli2vdi( int pli, int plimax )
{
  if (ks & 1) return pli;
  if (pli == plimax) return 1;
  if (pli > maxconv) return pli;
  return Pli2Vdi[pli];
}

static int vdi2pli( int vdi, int plimax )
{
  if ((ks & 1) == 0) return vdi;
  if (vdi == 1) return plimax;
  if (vdi > maxconv) return vdi;
  return Vdi2Pli[vdi];
}

static void setconv( void )
{
  static char vdi2pli[] = { 0,15,1,2,4,6,3,5,7,8,9,10,12,14,11,13 };
  int i, k;

  i = 255;
  do
  { char c = 0x80; c &= i;
    c += (i & (1 << 5)) << 1;
    c += (i & (1 << 3)) << 2;
    c += (i & (1 << 1)) << 3; half[i] = c;
    (unsigned char)c >>= 4; half[256 + i] = c;
    k = 0; if ((c = i) < 0) k += (1 << 15) + (1 << 14);
    if ((c <<= 1) < 0) k += (1 << 13) + (1 << 12);
    if ((c <<= 1) < 0) k += (1 << 11) + (1 << 10);
    if ((c <<= 1) < 0) k += (1 <<  9) + (1 <<  8);
    if ((c <<= 1) < 0) k += (1 <<  7) + (1 <<  6);
    if ((c <<= 1) < 0) k += (1 <<  5) + (1 <<  4);
    if ((c <<= 1) < 0) k += (1 <<  3) + (1 <<  2);
    if ((c <<= 1) < 0) k += 3; doub[i] = k;
  }
  while (--i >= 0);
  i = 255 * 2;
  do
  { k = *(int *)((char *)doub + i);
    ((int *)four)[i] = doub[(unsigned)k >> 8]; k &= 0xFF; k <<= 1;
    ((int *)four)[i + 1] = *(int *)((char *)doub + k);
  }
  while ((i -= 2) >= 0);
  i = 255; if (nplanes < 8) i = (1 << nplanes) - 1;
  k = 15; if (i < k || nplanes > k) i = k;
  maxconv = i;
  do
  { if (maxconv <= 15) k = vdi2pli[i];
    else
    {
      vsm_color( handle, i ); k = get_pix();
      if ((unsigned)maxconv < (unsigned)k) k = maxconv;
    }
    Vdi2Pli[i] = k;
    Pli2Vdi[k] = i;
  }
  while (--i >= 0);
}

static void switch_colors( VIEW *tv )
{
  int colors, k, *rgb_list, rgb_get[3];

  if ((tv->x_flag & 1) == 0) return;
  rgb_list = tv->rgb_list; if (*rgb_list++) return;
  colors = 1 << min( tv->planes, nplanes );
  for (k = 0; k < colors; ++k)
  {
    vq_color( handle, k, 0, rgb_get );
    vs_color( handle, k, rgb_list );
    *rgb_list++ = rgb_get[0];
    *rgb_list++ = rgb_get[1];
    *rgb_list++ = rgb_get[2];
  }
  tv->x_flag ^= 4;
}

static void write_palette( int xflag, int fh, int count, int *rgb_list )
{
  int	   pli, *rgb_ptr;
  unsigned char c[4];

  count = count / 3 - 1; pli = 0;
  do
  { rgb_ptr = rgb_list + 3 * pli2vdi( pli, count );
    if (xflag) Fwrite( fh, 6, rgb_ptr );
    else
    {
      c[0] = (unsigned char)((*rgb_ptr++ * 255L) / 1000);
      c[1] = (unsigned char)((*rgb_ptr++ * 255L) / 1000);
      c[2] = (unsigned char)((*rgb_ptr++ * 255L) / 1000);
      Fwrite( fh, 3, c );
  } }
  while (++pli <= count);
}

static void save_palette( VIEW *tv )
{
  long count, k, j;
  int  fh, i, *rgb_list;
  char *buf;

  rgb_list = tv->rgb_list; *rgb_list++ = 0;
  i = 1 << min( tv->planes, nplanes ); fh = 0;
  do vq_color( handle, fh, 0, rgb_list + fh * 3 ); while (++fh < i);
  graf_mouse( BUSYBEE, 0 );
  if ((fh = Fopen( tv->path, 2 )) < 0)
  {
    graf_mouse( ARROW, 0 ); form_error( -fh - 31 ); return;
  }
  i = (1 << tv->planes) * 3;
  if (tv->x_flag & 1)
  {
    if (tv->x_flag & 2)
    {
      CHUNK_HEADER ch;

      ch.length = sizeof(FORMCHUNK);
      do
      { Fseek( (ch.length + 1) & -2, fh, 1 );
	if (Fread( fh, 8, &ch ) < 8)
	{
	  Fclose( fh ); graf_mouse( ARROW, 0 );
	  form_error( ENOENT ); return;
	}
#ifndef __TOS__
	fliplongs( (int *)&ch.length, 1, 1 );
#endif
      }
      while (ch.magic != CM_MAGIC);
      write_palette( 0, fh, min( (int)ch.length, i ), rgb_list );
    }
    else
    {
      Fseek( sizeof(IMG_HEADER) + 4, fh, 0 );
#ifndef __TOS__
      flipwords( (char *)(rgb_list - 1), (i + 1) << 1 );
#endif
      Fwrite( fh, 2, rgb_list - 1 ); write_palette( 1, fh, i, rgb_list );
#ifndef __TOS__
      flipwords( (char *)(rgb_list - 1), (i + 1) << 1 );
#endif
  } }
  else
  {
    count = Fseek( 0, fh, 2 );
    if ((buf = Malloc( count )) == 0)
    {
      Fclose( fh ); graf_mouse( ARROW, 0 ); form_error( EINVMEM ); return;
    }
    Fseek( 0, fh, 0 ); count = Fread( fh, count, buf ); Fseek( 0, fh, 0 );
    if (tv->x_flag & 2)
    {
      k = 12;
      do
      {	if ((j = k) > count)
	{
	  Mfree( buf ); Fclose( fh ); graf_mouse( ARROW, 0 );
	  form_error( ENOENT ); return;
	}
#ifndef __TOS__
	fliplongs( (int *)(buf + j + 4), 1, 1 );
#endif
	k += (*(long *)(buf + j + 4) + 9) & -2;
#ifndef __TOS__
	fliplongs( (int *)(buf + j + 4), 1, 1 );
#endif
      }
      while (*(long *)(buf + j) != BM_MAGIC);
#ifndef __TOS__
      fliplongs( (int *)(buf + 4), 1, 1 );
#endif
      *(long *)(buf + 4) += i + 8;
#ifndef __TOS__
      fliplongs( (int *)(buf + 4), 1, 1 );
#endif
      Fwrite( fh, k, buf ); *(long *)buf = i;
#ifndef __TOS__
      fliplongs( (int *)buf, 1, 1 );
#endif
      Fwrite( fh, 4, "CMAP" ); Fwrite( fh, 4, buf );
      write_palette( 0, fh, i, rgb_list );
    }
    else
    {
#ifndef __TOS__
      flipwords( buf, sizeof(IMG_HEADER) );
#endif
      k = ((IMG_HEADER *)buf)->headlen << 1;
      ((IMG_HEADER *)buf)->headlen = 11 + i;
#ifndef __TOS__
      flipwords( buf, sizeof(IMG_HEADER) );
      flipwords( (char *)(rgb_list - 1), (i + 1) << 1 );
#endif
      Fwrite( fh, sizeof(IMG_HEADER), buf );
      Fwrite( fh, 4, "XIMG" );
      Fwrite( fh, 2, rgb_list - 1 ); write_palette( 1, fh, i, rgb_list );
#ifndef __TOS__
      flipwords( (char *)(rgb_list - 1), (i + 1) << 1 );
#endif
    }
    Fwrite( fh, count - k, buf + k ); Mfree( buf ); tv->x_flag |= 1;
  }
  Fclose( fh ); graf_mouse( ARROW, 0 );
}

static void transform( MFDB *src, MFDB *des )
{
  long size;

  *src = *des; size = 2L * src->fd_wdwidth * src->fd_h * src->fd_nplanes;
  if ((src->fd_addr = Malloc( size )) == 0)
  {
    if (src->fd_nplanes > 1) return;
    src->fd_addr = des->fd_addr;
  }
  else memcpy( src->fd_addr, des->fd_addr, size );
#ifndef __TOS__
  if (src->fd_stand) flipwords( src->fd_addr, size );
#endif
  vr_trnfm( handle, src, des );
#ifndef __TOS__
  if (des->fd_stand) flipwords( des->fd_addr, size );
#endif
  if (src->fd_addr != des->fd_addr) Mfree( src->fd_addr );
}

static void draw_image( VIEW *tv, int *clip )
{
  char *des;
  long size1, size2, lxy[4];
  int  i, k, index[2], pxy[8];
  VIEW *hv;
  MFDB d, s;

  *(long *)index = 0; ++*index;
  lxy[0] = ((long)tv->lbc << 3) + (clip[0] - tv->x);
  lxy[1] = ((long)tv->tlc << 3) + (clip[1] - tv->y);
  lxy[2] = lxy[0] + (clip[2] - clip[0]);
  lxy[3] = lxy[1] + (clip[3] - clip[1]);
  size1 = tv->mfdb.fd_w; size2 = tv->mfdb.fd_h;
  if ((i = tv->scale) > 0) { size1 <<= i; size2 <<= i; }
  if (i < 0) { size1 >>= -i; size2 >>= -i; }
  if (size1 <= lxy[2]) lxy[2] = --size1;
  if (size2 <= lxy[3]) lxy[3] = --size2;
  pxy[0] = (int)lxy[0]; pxy[1] = (int)lxy[1];
  pxy[2] = (int)lxy[2]; pxy[3] = (int)lxy[3];
  *(long *)(pxy + 4) = *(long *)clip;
  pxy[6] = clip[0] + pxy[2] - pxy[0];
  pxy[7] = clip[1] + pxy[3] - pxy[1];
  if (pxy[6] < clip[2] || pxy[7] < clip[3])
  {
    if (pxy[7] >= clip[3] && pxy[6] >= clip[0]) clip[0] = pxy[6] + 1;
    if (pxy[6] >= clip[2] && pxy[7] >= clip[1]) clip[1] = pxy[7] + 1;
    vr_recfl( handle, clip );
    if (pxy[6] < pxy[4] || pxy[7] < pxy[5]) return;
  }
  if (i == 0)
  {
    if (tv->mfdb.fd_stand) transform( &s, &tv->mfdb );
    if (tv->mfdb.fd_stand == 0)
    {
      d.fd_addr = 0;
      if (tv->mfdb.fd_nplanes == 1)
      {
	if (nplanes >= 16) switch_colors( tv );
	vrt_cpyfm( handle, MD_REPLACE, pxy, &tv->mfdb, &d, index );
	if (nplanes >= 16) switch_colors( tv );
      }
      else vro_cpyfm( handle, S_ONLY, pxy, &tv->mfdb, &d );
      return;
  } }
  if (tv->mfdb.fd_stand == 0) transform( &s, &tv->mfdb );
  d = tv->mfdb;
  if (i <= 0)
  {
    pxy[3] -= pxy[1]; pxy[1] = 0; d.fd_h = pxy[3] + 1;
    if (i < 0)
      { d.fd_w = ((pxy[2] >> 3) - (pxy[0] >> 3) + 1) << 3; k = 7; }
    else
      { d.fd_w = ((pxy[2] >> 4) - (pxy[0] >> 4) + 1) << 4; k = 15; }
  }
  else
  {
    d.fd_h = ((pxy[3] >> i) - (pxy[1] >> i) + 1) << i;
    k = (1 << i) - 1; i += 3; pxy[3] -= pxy[1] & ~k; pxy[1] &= k;
    d.fd_w = ((pxy[2] >> i) - (pxy[0] >> i) + 1) << i;
    k = (1 << i) - 1; i -= 3;
  }
  pxy[2] -= pxy[0] & ~k; pxy[0] &= k; d.fd_wdwidth = (d.fd_w + 15) >> 4;
  if (d.fd_stand == 0 ||
      (des = Malloc( 4L * d.fd_wdwidth * d.fd_h * d.fd_nplanes )) == 0)
  {
    vsf_interior( handle, FIS_USER ); vsf_color( handle, 1 );
    vr_recfl( handle, pxy + 4 );
    vsf_color( handle, 0 ); vsf_interior( handle, FIS_SOLID ); return;
  }
  size1 = tv->mfdb.fd_wdwidth << 1; size2 = size1 * tv->mfdb.fd_h;
  s = d; d.fd_addr = des; hv = tv;
  if (i < 0)
  {
    i = -i; size1 <<= i;
    (char *)s.fd_addr += size1 * lxy[1] + ((lxy[0] >> 3) << i);
    if (--i == 0)
    {
      char *phalf1, *phalf2; long c;
      phalf1 = half; phalf2 = phalf1 + 256; c = 0;
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ clip = (int *)tv; (char *)tv += size1; k = d.fd_wdwidth;
	  do
	  {
#ifdef __TOS__
	    char e;
	    (char)c = *((char *)clip)++; e = phalf1[c];
	    (char)c = *((char *)clip)++; e |= phalf2[c]; *des++ = e;
	    (char)c = *((char *)clip)++; e = phalf1[c];
	    (char)c = *((char *)clip)++; e |= phalf2[c]; *des++ = e;
#else
	    int e;
	    (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c]; e <<= 8;
	    (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c];
	    *((int *)des)++ = e;
#endif
	  }
	  while (--k);
	}
	while (--i);
      }
      while (--d.fd_nplanes);
    }
    else if (--i == 0)
    {
      char *phalf1, *phalf2; long c, e;
      phalf1 = half; phalf2 = phalf1 + 256; e = c = 0;
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ clip = (int *)tv; (char *)tv += size1; k = d.fd_wdwidth;
	  do
	  { (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c];
	    *des = phalf1[e];
	    (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c];
	    (char)e = phalf2[e]; *des++ |= e;
	    (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c];
	    *des = phalf1[e];
	    (char)c = *((char *)clip)++; (char)e = phalf1[c];
	    (char)c = *((char *)clip)++; (char)e |= phalf2[c];
	    (char)e = phalf2[e]; *des++ |= e;
	  }
	  while (--k);
	}
	while (--i);
      }
      while (--d.fd_nplanes);
#ifndef __TOS__
      flipwords( d.fd_addr, des - d.fd_addr );
#endif
    }
    else
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ clip = (int *)tv; (char *)tv += size1; k = d.fd_wdwidth;
	  do
	  { int e = 0;
	    if (*((char *)clip)++ < 0) e += 1 << 15;
	    if (*((char *)clip)++ < 0) e += 1 << 14;
	    if (*((char *)clip)++ < 0) e += 1 << 13;
	    if (*((char *)clip)++ < 0) e += 1 << 12;
	    if (*((char *)clip)++ < 0) e += 1 << 11;
	    if (*((char *)clip)++ < 0) e += 1 << 10;
	    if (*((char *)clip)++ < 0) e += 1 <<  9;
	    if (*((char *)clip)++ < 0) e += 1 <<  8;
	    if (*((char *)clip)++ < 0) e += 1 <<  7;
	    if (*((char *)clip)++ < 0) e += 1 <<  6;
	    if (*((char *)clip)++ < 0) e += 1 <<  5;
	    if (*((char *)clip)++ < 0) e += 1 <<  4;
	    if (*((char *)clip)++ < 0) e += 1 <<  3;
	    if (*((char *)clip)++ < 0) e += 1 <<  2;
	    if (*((char *)clip)++ < 0) e += 1 <<  1;
	    if (*((char *)clip)++ < 0) e += 1;
	    *((int *)des)++ = e;
	  }
	  while (--k);
	}
	while (--i);
      }
      while (--d.fd_nplanes);
  }
  else if (i == 0)
  {
    (char *)s.fd_addr += size1 * lxy[1] + ((lxy[0] >> 4) << 1);
    do
    { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
      do
      { clip = (int *)tv; (char *)tv += size1; k = d.fd_wdwidth;
	do *((int *)des)++ = *clip++; while (--k);
      }
      while (--i);
    }
    while (--d.fd_nplanes);
#ifndef __TOS__
    flipwords( d.fd_addr, des - d.fd_addr );
#endif
  }
  else
  {
    (char *)s.fd_addr += size1 * (lxy[1] >> i) + ((lxy[0] >> 3) >> i);
    if (--i == 0)
    {
      int *pdoub = doub;
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ unsigned char *src = (unsigned char *)tv; (char *)tv += size1;
	  k = d.fd_wdwidth; k <<= 1; (char *)clip = des + k;
	  do
	  { *(int *)des = pdoub[*src++]; *clip++ = *((int *)des)++;
	  }
	  while (k -= 2);
	  des = (char *)clip;
	}
	while (i -= 2);
      }
      while (--d.fd_nplanes);
    }
    else if (--i == 0)
    {
      long *pfour = four;
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ unsigned char *src = (unsigned char *)tv; (char *)tv += size1;
	  clip = (int *)des; k = d.fd_wdwidth;
	  do *((long *)des)++ = pfour[*src++]; while (k -= 2);
	  k = d.fd_wdwidth;
	  do
	  { *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	  }
	  while (k -= 2);
	}
	while (i -= 4);
      }
      while (--d.fd_nplanes);
    }
    else
    {
      char c = -1;
      do
      { tv = s.fd_addr; (char *)s.fd_addr += size2; i = d.fd_h;
	do
	{ char *src = (char *)tv; (char *)tv += size1;
	  clip = (int *)des; k = d.fd_wdwidth;
	  do
	  { char e; if ((e = *src++) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	    if ((e <<= 1) < 0) *des++ = c; else *des++ = 0;
	  }
	  while (k -= 4);
	  k = d.fd_wdwidth;
	  do
	  { *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	    *((long *)des)++ = *((long *)clip)++;
	  }
	  while (k -= 2);
	}
	while (i -= 8);
      }
      while (--d.fd_nplanes);
#ifndef __TOS__
      flipwords( d.fd_addr, des - d.fd_addr );
#endif
  } }
  tv = hv; d.fd_nplanes = s.fd_nplanes; s.fd_addr = des;
  des = d.fd_addr; vr_trnfm( handle, &d, &s ); d.fd_addr = 0;
  if (s.fd_nplanes == 1)
  {
    if (nplanes >= 16) switch_colors( tv );
    vrt_cpyfm( handle, MD_REPLACE, pxy, &s, &d, index );
    if (nplanes >= 16) switch_colors( tv );
  }
  else vro_cpyfm( handle, S_ONLY, pxy, &s, &d );
  Mfree( des );
}

static void change_scale( VIEW *tv, int mx, int my, int delta )
{
  int dx, dy, d[3];

  if (mx < 0) { graf_mkstate( d + 1, d + 2, d, d ); mx = d[1]; my = d[2]; }
  dx = tv->width; dy = tv->height;
  switch (tv->scale += delta)
  {
    case -3: ++dx; dx >>= 1; ++dy; dy >>= 1;
    case -2: ++dx; dx >>= 1; ++dy; dy >>= 1;
    case -1: ++dx; dx >>= 1; ++dy; dy >>= 1;
    case  0: ++dx; dx >>= 1; ++dy; dy >>= 1;
    case  1: ++dx; dx >>= 1; ++dy; dy >>= 1;
    case  2: ++dx; dx >>= 1; ++dy; dy >>= 1;
  }
  dx -= tv->w >> 3; tv->bc = dx; dy -= tv->h >> 3; tv->lc = dy;
  dx = tv->lbc; mx -= tv->x; mx += 3; mx >>= 3; dx += mx;
  dy = tv->tlc; my -= tv->y; my += 3; my >>= 3; dy += my;
  if (delta > 0) { dx <<= 1; dy <<= 1; } else { dx >>= 1; dy >>= 1; }
  dx -= mx; if (tv->bc < dx) dx = tv->bc; if (dx < 0) dx = 0;
  dy -= my; if (tv->lc < dy) dy = tv->lc; if (dy < 0) dy = 0;
  tv->lbc = dx; tv->tlc = dy; set_allslider(); full_redraw();
}

static void free_image() { }

#pragma warn -par
static int key_image( VIEW *tv, int code, int ks )
{
  switch (code)
  {
    case CNTRL_CL: if (tv->scale >= -2) change_scale(tv,-1,0,-1); return 0;
    case CNTRL_CR: if (tv->scale <=  2) change_scale(tv,-1,0, 1); return 0;
    case CNTRL_E: if ((tv->x_flag & 4) == 0) switch_colors( tv ); return 0;
    case CNTRL_S: if (tv->x_flag & 4) switch_colors( tv ); return 0;
    case CNTRL_X: save_palette( tv ); return 0;
  }
  return 1;
}
#pragma warn +par

static void sclick_image( VIEW *tv, int mx, int my, int flag )
{
  if (flag) return;
  popup[5].ob_state = tv->x_flag & 1 ? NORMAL : DISABLED;
  popup[5].ob_spec.free_string = tv->x_flag & 4 ? sstring : ostring;
  popup[4].ob_state = tv->scale <=  2 ? NORMAL : DISABLED;
  popup[2].ob_state = tv->scale >= -2 ? NORMAL : DISABLED;
  {
    char v = ' ', z = '0', *p = string2;
    if (tv->scale < 0) { v = '-'; z -= tv->scale; }
    if (tv->scale > 0) { v = '+'; z += tv->scale; }
    *p++ = v; *p++ = z; *p++ = v;
  }
  itostring( tv->width, string3 + 5, 6 );
  itostring( tv->height, string3 + 12, 6 );
  itostring( tv->planes, string4 + 7, 3 );
  strncpy( string5 + 10, tv->x_flag & 2 ? "CMAP" : "XIMG", 4 );
  switch (popup_menu( popup, 3, mx, my, objc_draw ))
  {
    case 2: change_scale( tv, mx, my, -1 ); break;
    case 4: change_scale( tv, mx, my,  1 ); break;
    case 5: switch_colors( tv ); break;
    case 9: save_palette( tv );
} }

static VIEW *load_image( int fh, int planes, int height, int width,
			 int special, int headlen )
{
  VIEW *rv;
  char cdata, *line_buf, *endl_buf, *line_ptr,
       *raster_ptr, *plane_ptr, *buf_ptr;
  int  idata, i, linelen, line, vrc, ditherflag;
  long bufsize, planesize, rastsize;
  MFDB s;
  FBUF f;

  s.fd_wdwidth = (width + 15) >> 4; linelen = s.fd_wdwidth << 1;
  s.fd_w = (width + 7) & -8; s.fd_h = (height + 7) & -8;
  bufsize = (long)linelen * planes;
  planesize = (long)linelen * s.fd_h;
  s.fd_nplanes = planes > 1 ? nplanes : 1;
  i = 0; if (s.fd_nplanes >= 16) i = 1 - (s.fd_nplanes >> 3);
  if (s.fd_nplanes < planes && (i = par.dithcol) != 0)
  {
    if (s.fd_nplanes < 3) i = 1;
    if (i == 1) s.fd_nplanes = 1;
  }
  rastsize = planesize * s.fd_nplanes;
  idata = 1 << planes; line = idata * 6 + 2;
  if ((ditherflag = i) != 0)
  {
    i = idata;
    if (ditherflag != 1) { i <<= 1; if (ditherflag != -1) i <<= 1; }
  }
  if ((rv = Malloc( sizeof(VIEW) + line + rastsize )) == 0)
    { Fclose( fh ); form_error( EINVMEM ); return 0; }
  if ((buf_ptr = Malloc( bufsize + i )) == 0)
    { Mfree( rv ); Fclose( fh ); form_error( EINVMEM ); return 0; }
  if (popup->ob_next == 0)
    { --popup->ob_next; fix_tree( popup, 9 ); setconv(); }
  rv->planes = planes; rv->width = width; rv->height = height;
  width = headlen ? (s.fd_w >> 3) : linelen;
  line_buf = (char *)rv->rgb_list;
  raster_ptr = line_buf + line;
  endl_buf = buf_ptr + bufsize; --idata;
  if (headlen)	/* IMG */
  {
    i = 0;
    if (headlen >= 10)
    {
      Fread( fh, 4, line_buf );
      if (*(long *)line_buf == X_MAGIC)
      {
	Fread( fh, 2, line_buf );
#ifndef __TOS__
	flipwords( line_buf, 2 );
#endif
	line_buf += 2;
	do
	{ line_ptr = line_buf + 6 * pli2vdi( i, idata );
	  Fread( fh, 6, line_ptr );
#ifndef __TOS__
	  flipwords( line_ptr, 6 );
#endif
	  switch (ditherflag)
	  {
	    case -3:
	    case -2:
	      get_true( (int *)line_ptr,
			(long *)endl_buf + vdi2pli( i, idata ) );
	      break;
	    case -1:
	      get_true( (int *)line_ptr,
			(int *)endl_buf + vdi2pli( i, idata ) );
	      break;
	    case 1:
	      endl_buf[vdi2pli( i, idata )] = gray( (int *)line_ptr );
	      break;
	    case 2:
	      plane_ptr = endl_buf + (vdi2pli( i, idata ) << 2);
	      *plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	      *plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	      *plane_ptr   = (*(int *)line_ptr     << 3) / 125;
	} }
	while (++i <= idata);
	i = 1;
    } }
    Fseek( headlen << 1, fh, 0 );
  }
  else		/* IFF */
  {
    i = 2;
    for (;;)
    {
      CHUNK_HEADER ch;

      if (Fread( fh, 8, &ch ) < 8)
      {
        Mfree( buf_ptr ); Mfree( rv ); Fclose( fh );
        form_error( ENOENT ); return 0;
      }
#ifndef __TOS__
      fliplongs( (int *)&ch.length, 1, 1 );
#endif
      if (ch.magic == BO_MAGIC) break;
      if (ch.magic == CM_MAGIC)
      {
	*((int *)line_buf)++ = 0; i = 0;
	do
	{ Fread( fh, 3, f.sbuf );
	  line_ptr = line_buf + 6 * pli2vdi( i, idata );
	  *((int *)line_ptr)++ = (int)((f.sbuf[0] * 200L) / 51);
	  *((int *)line_ptr)++ = (int)((f.sbuf[1] * 200L) / 51);
	  *(int *)line_ptr     = (int)((f.sbuf[2] * 200L) / 51);
	  line_ptr -= 4;
	  switch (ditherflag)
	  {
	    case -3:
	    case -2:
	      get_true( (int *)line_ptr,
			(long *)endl_buf + vdi2pli( i, idata ) );
	      break;
	    case -1:
	      get_true( (int *)line_ptr,
			(int *)endl_buf + vdi2pli( i, idata ) );
	      break;
	    case 1:
	      endl_buf[vdi2pli( i, idata )] = gray( (int *)line_ptr );
	      break;
	    case 2:
	      plane_ptr = endl_buf + (vdi2pli( i, idata ) << 2);
	      *plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	      *plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	      *plane_ptr   = (*(int *)line_ptr     << 3) / 125;
	} }
	while ((ch.length -= 3) > 0 && ++i <= idata);
	i = 3;
      }
      Fseek( (ch.length + 1) & -2, fh, 1 );
  } }
  switch (ditherflag)
  {
    case -3:
    case -2:
      if ((i & 1) == 0)
      {
	get_true( defcols[15], (long *)endl_buf + idata );
	while (--idata >= 0)
	  get_true( defcols[idata & 15], (long *)endl_buf + idata );
      }
      else
      {
	vs_color( handle, 1, defcols[0] );
	v_pmarker( handle, 1, defcols[15] );
      }
      vs_color( handle, 1, defcols[15] ); break;
    case -1:
      if ((i & 1) == 0)
      {
	get_true( defcols[15], (int *)endl_buf + idata );
	while (--idata >= 0)
	  get_true( defcols[idata & 15], (int *)endl_buf + idata );
      }
      else
      {
	vs_color( handle, 1, defcols[0] );
	v_pmarker( handle, 1, defcols[15] );
      }
      vs_color( handle, 1, defcols[15] ); break;
    case 1: if (i & 1) break;
      endl_buf[idata] = 0;
      while (--idata >= 0)
	endl_buf[idata] = gray( defcols[idata & 15] );
      break;
    case 2: if (i & 1) break;
      ((long *)endl_buf)[idata] = 0;
      while (--idata >= 0)
      {
	plane_ptr = endl_buf + (idata << 2);
	(int *)line_ptr = defcols[idata & 15];
	*plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	*plane_ptr++ = (*((int *)line_ptr)++ << 3) / 125;
	*plane_ptr   = (*(int *)line_ptr     << 3) / 125;
  }   }
  rv->x_flag = i; rv->scale = 0;
  rv->mfdb.fd_addr = raster_ptr;
  rv->mfdb.fd_stand = 0; if (ditherflag >= 0) ++rv->mfdb.fd_stand;
  rv->mfdb.fd_nplanes = s.fd_nplanes;
  rv->mfdb.fd_wdwidth = s.fd_wdwidth;
  *(long *)&rv->w = *(long *)&rv->mfdb.fd_w = *(long *)&s.fd_w;
  rv->bc = rv->w >> 3; rv->lc = rv->h >> 3; rv->xfac = rv->yfac = 8;
  rv->draw = draw_image; rv->free = free_image;
  rv->key = key_image; rv->sclick = sclick_image;
  f.handle = fh; Fbufopen( &f );
  memset( raster_ptr, 0, rastsize ); line = 0;
  for (;;)
  {
    vrc = 1; line_buf = buf_ptr;
    do
    { line_ptr = line_buf; line_buf += width;
      do
	if (headlen)						/* IMG */
	  switch (idata = Fgetc( &f ))
	  {
	    case 0:    if ((idata = Fgetc( &f )) != 0)	/* pattern run */
		       {
			 plane_ptr = line_ptr;
			 for (i = special; --i >= 0;)
			   *line_ptr++ = Fgetc( &f );
			 while (--idata > 0)
			   for (i = special; --i >= 0;)
			     *line_ptr++ = *plane_ptr++;
			 break;
		       }			/* vertical replication */
		       if (Fgetc( &f ) == 0xFF) vrc = Fgetc( &f ); break;
	    case 0x80: idata = Fgetc( &f );		  /* bit string */
		       while (--idata >= 0) *line_ptr++ = Fgetc( &f );
		       break;
	    default:   if (idata < 0)	      /* unexpected end of file */
			 { line_ptr = line_buf; break; }
		       i = 0; if ((char)idata < 0) --i;    /* solid run */
		       (char)idata <<= 1;
		       do *line_ptr++ = i; while (idata -= 2);
	  }
	else if (special == 0) *line_ptr++ = Fgetc( &f );	/* IFF */
	else if ((cdata = Fgetc( &f )) >= 0)	     /* IFF compressed */
	     { do *line_ptr++ = Fgetc( &f ); while (--cdata >= 0); }
	     else
	     { i = Fgetc( &f ); do *line_ptr++ = i; while (++cdata <= 0); }
      while (line_ptr < line_buf);
    }
    while ((line_buf += width & 1) < endl_buf);
    vrc += line;
    do
    { line_ptr = buf_ptr; i = linelen;
      switch (ditherflag)
      {
	case -3:
	  do
	  { cdata = 0x80;
	    do
	    { int pix = 0; idata = 4; plane_ptr = line_ptr;
	      do
	      { if (*plane_ptr & cdata) pix |= idata;
		idata <<= 1;
	      }
	      while ((plane_ptr += linelen) < endl_buf);
	      *((long *)raster_ptr)++ = *(long *)(endl_buf + pix);
	    }
	    while ((unsigned char)cdata >>= 1);
	    ++line_ptr;
	  }
	  while (--i);
	  break;
	case -2:
	  do
	  { cdata = 0x80;
	    do
	    { int pix = 0; idata = 4; plane_ptr = line_ptr;
	      do
	      { if (*plane_ptr & cdata) pix |= idata;
		idata <<= 1;
	      }
	      while ((plane_ptr += linelen) < endl_buf);
	      plane_ptr = endl_buf + pix;
	      *raster_ptr++ = *plane_ptr++;
	      *raster_ptr++ = *plane_ptr++;
	      *raster_ptr++ = *plane_ptr++;
	    }
	    while ((unsigned char)cdata >>= 1);
	    ++line_ptr;
	  }
	  while (--i);
	  break;
	case -1:
	  do
	  { cdata = 0x80;
	    do
	    { int pix = 0; idata = 2; plane_ptr = line_ptr;
	      do
	      { if (*plane_ptr & cdata) pix |= idata;
		idata <<= 1;
	      }
	      while ((plane_ptr += linelen) < endl_buf);
	      *((int *)raster_ptr)++ = *(int *)(endl_buf + pix);
	    }
	    while ((unsigned char)cdata >>= 1);
	    ++line_ptr;
	  }
	  while (--i);
	  break;
	case 0: idata = 1;
	  do
	  { if (--idata) line_buf += planesize;
	    else { line_buf = raster_ptr; idata = s.fd_nplanes; }
	    do *((int *)line_buf)++ |= *((int *)line_ptr)++;
	    while (i -= 2);
	    line_buf -= i = linelen;
	  }
	  while (line_ptr < endl_buf);
	  if (planes >= s.fd_nplanes) { raster_ptr += linelen; break; }
	  do
	  { int mask; idata = 1;
	    line_ptr = raster_ptr; mask = *((int *)raster_ptr)++;
	    do
	    { line_ptr += planesize;
	      if (idata < planes) mask &= *(int *)line_ptr;
	      else *(int *)line_ptr = mask;
	    }
	    while (++idata < s.fd_nplanes);
	  }
	  while (i -= 2);
	  break;
	case 1: line_buf = dithermatrix + ((line & 7) << 3);
	  do
	  { cdata = 0x80;
	    do
	    { int pix = 0; idata = 1; plane_ptr = line_ptr;
	      do
	      { if (*plane_ptr & cdata) pix |= idata;
		idata <<= 1;
	      }
	      while ((plane_ptr += linelen) < endl_buf);
	      if (endl_buf[pix] <= *line_buf++) *raster_ptr |= cdata;
	    }
	    while ((unsigned char)cdata >>= 1);
	    line_buf -= 8; ++raster_ptr; ++line_ptr;
	  }
	  while (--i);
	  break;
	case 2: line_buf = dithermatrix + ((line & 7) << 3);
	  do
	  { cdata = 0x80;
	    do
	    { int pix = 0; idata = 4; plane_ptr = line_ptr;
	      do
	      { if (*plane_ptr & cdata) pix |= idata;
		idata <<= 1;
	      }
	      while ((plane_ptr += linelen) < endl_buf);
	      plane_ptr = endl_buf + pix; pix = 0;
	      (char)idata = *line_buf++;
	      if (*plane_ptr++ > (char)idata) pix += 4; /* Rot  */
	      if (*plane_ptr++ > (char)idata) pix += 2; /* Grn */
	      if (*plane_ptr++ > (char)idata) pix += 1; /* Blau */
	      plane_ptr = raster_ptr;
	      if ((pix <<= 4) == 0)	/* Schwarz */
	      {
		idata = s.fd_nplanes;
		do { *plane_ptr |= cdata; plane_ptr += planesize; }
		while (--idata);
	      }
	      else if (pix != 0x70)	/* nicht Weiss */
	      {
		if (((char)pix <<= 1) < 0) *plane_ptr |= cdata;
		plane_ptr += planesize;
		if (((char)pix <<= 1) < 0) *plane_ptr |= cdata;
		plane_ptr += planesize;
		if (((char)pix <<= 1) < 0) *plane_ptr |= cdata;
	    } }
	    while ((unsigned char)cdata >>= 1);
	    line_buf -= 8; ++raster_ptr; ++line_ptr;
	  }
	  while (--i);
      }
      if (++line == height)
      {
	Fbufclose( &f ); Mfree( buf_ptr ); return rv;
    } }
    while (line < vrc);
} }

VIEW *load_iff( int fh )
{
  FORMCHUNK fm;
  BMHDCHUNK bm;

  Fread( fh, sizeof(FORMCHUNK), &fm );
  if (fm.fm_magic != FM_MAGIC || fm.fm_type != FM_IMAGE)
    { Fclose( fh ); form_error( ENOENT ); return 0; }
  for (;;)
  {
    if (Fread( fh, 8, &bm ) < 8)
      { Fclose( fh ); form_error( ENOENT ); return 0; }
    if (bm.bm_magic == BM_MAGIC) break;
#ifndef __TOS__
    fliplongs( (int *)&bm.bm_length, 1, 1 );
#endif
    Fseek( (bm.bm_length + 1) & -2, fh, 1 );
  }
  Fread( fh, 20, &bm.bm_width );
#ifndef __TOS__
  flipwords( (char *)&bm.bm_width, 8 );
#endif
  return load_image( fh, bm.bm_nPlanes, bm.bm_height, bm.bm_width,
		     bm.bm_Compression, 0 );
}

VIEW *load_img( int fh )
{
  IMG_HEADER header;

  Fread( fh, sizeof(IMG_HEADER), &header );
#ifndef __TOS__
  flipwords( (char *)&header, sizeof(IMG_HEADER) );
#endif
  return load_image( fh, header.planes, header.sl_height, header.sl_width,
		     header.pat_run, header.headlen );
}

#pragma warn -sig
void out_image( char *name )
{
  int        fh;
  IMG_HEADER header;

  header.sl_width = xpixel; header.sl_height = ypixel;
  if (par.aspect != 1)
  {
    if ((fh = Fopen( name, 0 )) < 0) { form_error( -fh - 31 ); return; }
    Fread( fh, sizeof(IMG_HEADER), &header ); Fclose( fh );
  }
  printbox( name, 0 ); fh = 0;
  if (par.aspect == 2)
  {
    fh = 1;
    if (*(long *)&par.x_scale)
    {
      header.sl_width *= (header.pix_width - 1) / out_width + 1;
      header.sl_height *= (header.pix_height - 1) / out_height + 1;
    }
    else
    {
      header.sl_width =
	((long)header.sl_width * header.pix_width) / out_width;
      header.sl_height =
	((long)header.sl_height * header.pix_height) / out_height;
  } }
  header.pix_width = 0;
  if (par.h_align) header.pix_width = xpixel - header.sl_width;
  if (par.h_align == 1) header.pix_width >>= 1;
  header.pix_height = 0;
  if (par.v_align) header.pix_height = ypixel - header.sl_height;
  if (par.v_align == 1) header.pix_height >>= 1;
  header.sl_width += header.pix_width - 1;
  header.sl_height += header.pix_height - 1;
  v_bit_image( ohandle, name, fh, par.x_scale, par.y_scale,
	       par.h_align, par.v_align, &header.pix_width );
  v_updwk( ohandle ); v_clrwk( ohandle ); printbox( 0, 0 );
}