|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Payments Humor Music |
Bitmap editor for the Heathkit H-100 computer, in DeSmet CEDIT.C is a bitmap editor in DeSmet C for the Heathkit H-100 computer. It allows creating and editing graphic images. The interface provides methods for using all of my H-100 graphics functions (pixel set, draw lines, boxes, circles, ovals, flood fill with dithering), plus some additional ones contained within the program (save an area and move or copy it with optional scaling, rotation, and translation). It uses the H-100's second video page for the Help screen and as a scratch pad for some of the graphics operations. |
/* EDIT.C 5-24-93
Copyright (C)1993 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Version 3 as published by the Free Software Foundation.
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.
H-100 GRAPHICS SCREEN (BITMAP) EDITOR.
6-8-93 INCORPORATED ALL FEATURES FROM PROGRAM SCALER.C
6-9-93 WHEN LOADED FILE IS ROTATED, IT AUTOMATICALLY LANDS ON SCREEN
6-10-93 CAN USE PAGE 2 FOR LOADING FILES AND AS A SCRATCHPAD
10-31-93 exec("command.com") doesn't work: scrolling destroys pg.2
11-24-93 paint now allows multiple border colors
12-3-93 (e)xcept option for border colors
12-5-93 supports new pset()
drawing lines with 'xpset' still doesn't work properly because
the ends of 2 line segments actually overlap at their intersection
12-25-93 read/write graphics area to disk
5-2-94 moved painting into dopaint(), with tile or spray
5-9-94 allows entry of xstep, ystep
LIST OF THINGS TO DO:
add color at (x,y) to status line display
add ^C abort to Fill (must do it in box()), and add to circle()
allow entry of angle of x/y, while keeping drawn line length the same in
all directions (?).
move code out of cases in main() & into functions: especially case 'g'
get file, and case '|' change paint border - move border setting into
dopaint(), but leave border defined in main() so status line can show it.
ADD GOTO X3Y3 SO YOU CAN RETURN TO A PLACE YOU WERE AT AND SAVED
ADD MACROS AND MACROS WITH A REPETITION COUNT. (SEE NET.C)
FOR REP. COUNT, MOVE MOST OF MAIN() INTO PARSE(),
IN MAIN, ASSIGN A STRING OF CHARS TO DESIRED FUNCTION KEY(S),
THEN PICK OFF THE CHARS FROM THE STRING AND SEND TO PARSE().
ALLOW CURSOR TO *SHOW* WHERE TO BEGIN LOADED FILE.
NOTES:
-->THIS PROGRAM PROBABLY USES MOST OR ALL OF THE FUNCTIONS FROM MY GRAPHICS FUNCTION LIBRARIES.
--Some of the operations are overly ambitious for the H100, and can take several minutes to complete.
*/
#include "stdio.h"
/* H100 keyboard keycodes */
/* UNSHIFTED KEYPAD */
#define KEY1 0xB1
#define KEY2 0xB2
#define KEY3 0xB3
#define KEY4 0xB4
#define KEY5 0xB5
#define KEY6 0xB6
#define KEY7 0xB7
#define KEY8 0xB8
#define KEY9 0xB9
/* SHIFTED KEYPAD */
#define SHIFTKEY0 0xF0
#define SHIFTKEY1 0xF1
#define SHIFTKEY2 0xF2
#define SHIFTKEY3 0xF3
#define SHIFTKEY4 0xF4
#define SHIFTKEY5 0xF5
#define SHIFTKEY6 0xF6
#define SHIFTKEY7 0xF7
#define SHIFTKEY8 0xF8
/* ARROW KEYS */
#define UP 0xA5
#define DOWN 0xA6
#define LEFT 0xA8
#define RIGHT 0xA7
/* SHIFTED ARROW KEYS */
#define SHIFTUP 0xE5
#define SHIFTDOWN 0xE6
#define SHIFTLEFT 0xE8
#define SHIFTRIGHT 0xE7
/* OTHER KEYPAD KEYS */
#define KEYPERIOD 0xAE
#define KEYMINUS 0xAD
#define HOME 0xA9
#define SHIFTHOME 0xE9
#define ENTER 0x8D
/* OTHER KEYS */
#define F1 0x97
#define F2 0x98
#define F3 0x99
#define F4 0x9A
#define F9 0x9F
#define HELP 0x95
/* VRAM SEGMENTS */
#define BLUE 0xC000
#define RED 0xD000
#define GREEN 0xE000
/* ----------------------------------------------------------------------- */
/* GLOBAL DATA */
/* ----------------------------------------------------------------------- */
char gbuf[2160]; /* HOLDS GRAPHICS CONTENTS OF LINE 25 */
struct areadesc /* DESCRIPTION OF AREA OF SCREEN STORED TO BUFFER */
{
char *address; /* POINTER TO START OF ALLOCATED CHAR ARRAY */
int xspan; /* X AXIS (WIDTH), IN PIXELS */
int yspan; /* Y AXIS (HEIGHT), IN PIXELS */
} area;
/* LIST OF THE DISPLAYED H100 SCAN LINES */
unsigned scanline[225] =
{
0,1,2,3,4,5,6,7,8,16,17,18,19,20,21,22,23,24,32,33,34,35,36,37,38,39,40,
48,49,50,51,52,53,54,55,56,64,65,66,67,68,69,70,71,72,80,81,82,83,84,85,
86,87,88,96,97,98,99,100,101,102,103,104,112,113,114,115,116,117,118,119,
120,128,129,130,131,132,133,134,135,136,144,145,146,147,148,149,150,151,
152,160,161,162,163,164,165,166,167,168,176,177,178,179,180,181,182,183,
184,192,193,194,195,196,197,198,199,200,208,209,210,211,212,213,214,215,
216,224,225,226,227,228,229,230,231,232,240,241,242,243,244,245,246,247,
248,256,257,258,259,260,261,262,263,264,272,273,274,275,276,277,278,279,
280,288,289,290,291,292,293,294,295,296,304,305,306,307,308,309,310,311,
312,320,321,322,323,324,325,326,327,328,336,337,338,339,340,341,342,343,
344,352,353,354,355,356,357,358,359,360,368,369,370,371,372,373,374,375,
376,384,385,386,387,388,389,390,391,392
};
/* -------------------------------------------------------------- */
/* clrscr() ERASES ENTIRE SCREEN, INCLUDING LINE 25 */
/* -------------------------------------------------------------- */
void clrscr()
{
locate(25,1); puts("\033E"); /* CLEAR 25TH LINE */
locate(1,1); puts("\033E"); /* CLEAR REST OF SCREEN */
return; /* must leave on line 1 for drawhelp() */
}
/* -------------------------------------------------------------- */
/* NEGPLANE() 6-8-93 */
/* INVERTS VRAM DATA IN ONE OF THE COLOR PLANES */
/* INPUT IS CHAR = G,R,B OR A FOR ALL */
/* RETURNS: VOID */
/* 10-31-93 USING SCANLINES[] MADE 2 SEC. (20%) IMPROVEMENT */
/* 12-8-93 using _lmove brought 63% speed improvement */
/* -------------------------------------------------------------- */
void negplane(ch)
char ch;
{
int j, k;
unsigned segment, dataseg;
char *offset, buf[80];
dataseg = _showds();
switch(tolower(ch))
{
case 'b':
segment = BLUE;
break;
case 'r':
segment = RED;
break;
case 'a': /* all: first DO blue AND RED */
negplane('b');
if(csts() == 3) /* ^C = don't do any more */
return;
negplane('r');
if(csts() == 3)
return;
case 'g': /* then FALL THROUGH FOR green */
segment = GREEN;
break;
default:
return;
break;
}
for(j = 0 ; j < 225 ; j++) /* FOR EACH SCAN LINE, */
{
offset = scanline[j] * 128; /* OFFSET OF 1ST BYTE OF THE LINE */
_lmove(80,offset,segment,buf,dataseg); /* move 80 bytes to local buf */
for(k = 0 ; k < 80 ; k++) /* INVERT them all */
buf[k] = ~(buf[k]);
_lmove(80,buf,dataseg,offset,segment); /* move back to vram */
}
return;
} /* end negplane() */
/* ----------------------------------------------------------------------- */
/* GETS INPUT FILE NAME FROM USER */
/* ----------------------------------------------------------------------- */
void inpnam(fnam)
char *fnam;
{
puts("\033y5File name (include .ZIC/.ZED) (CR=quit): "); /* CURSOR ON */
scanf("%50s",fnam);
strupr(fnam); /* TO UPPER FOR LATER DISPLAY */
puts("\033x5"); /* CURSOR OFF */
return;
}
/* -------------------------------------------------------------- */
/* GETAREA() SAVES AREA OF SCREEN TO MEMORY FOR RELOCATION */
/* EACH PIXEL IS SAVED AS A BYTE IN A CHAR ARRAY WITH A VALUE 0-7 */
/* WASTEFUL OF MEMORY, BUT IT WORKS */
/* RETURNS TRUE IF SUCCEED, FALSE IF OFFSCREEN OR TOO LARGE TO STORE */
/* -------------------------------------------------------------- */
int getarea(x1,y1,x2,y2)
int x1, y1, x2, y2; /* 0 TO 639 AND 0 TO 224 */
{
int i, j; /* COUNTERS */
char *p; /* POINTER INTO ALLOCATED ARRAY */
/* CHARS ARE BEING USED AS SHORT INTEGERS */
if((x1 < 0) || (x1 > 639) || (x2 < 0) || (x2 > 639))
return(FALSE);
if((y1 < 0) || (y1 > 224) || (y2 < 0) || (y2 > 224))
return(FALSE);
if(area->address) /* IF BUFFER ALREADY IN USE, */
free(area->address); /* FREE IT FOR REUSE */
if(y1 > y2) {i = y1; y1 = y2; y2 = i;} /* Y1 = LOW, Y2 = HIGH */
if(x1 > x2) {i = x1; x1 = x2; x2 = i;} /* X1 = LOW, X2 = HIGH */
area->yspan = y2 - y1 + 1; /* AREA HEIGHT */
area->xspan = x2 - x1 + 1; /* AREA WIDTH */
/* if area is too big, instead of failing, could save to disk and somehow */
/* flag that it's on disk: make 1st char of area->address = 255? */
if(((long)(area->xspan)*(long)(area->yspan)) > 65535)
return(FALSE);
if(!(area->address = malloc(area->yspan * area->xspan)))
return(FALSE);
p = area->address;
for(i = y1 ; i <= y2 ; i++)
for(j = x1 ; j <= x2 ; j++)
*(p++) = (char)pset(j,i,'s',0);
return(TRUE);
}
/* -------------------------------------------------------------- */
/* PUTAREA() COPIES AREA FROM MEMORY TO SCREEN DISPLAY */
/* returns TRUE if successful, FALSE if not */
/* 12-25-93 ok if area runs off screen edges */
/* -------------------------------------------------------------- */
int putarea(x0,y0,psetmode)
int x0, y0; /* UPPER LEFT CORNER OF WHERE TO PLACE AREA */
char psetmode; /* (P)SET, (X)PSET, OR (O)PSET */
{
int x, y, highx, highy;
char *p;
if(!(p = area->address)) /* NOTHING THERE TO PUT */
return(FALSE);
/* could test 1st char of area->address */
/* if it's 255, get area from disk */
highx = x0 + area->xspan;
highy = y0 + area->yspan;
for(y = y0 ; y < highy ; y++)
for(x = x0 ; x < highx ; x++)
pset(x,y,psetmode,*(p++));
return(TRUE);
}
/* -------------------------------------------------------------- */
/* writearea() writes graphics area to disk in format: */
/* width,height\n, then a series of (width*height) chars: */
/* color at x1,y1, color at x2,y1, etc. (x varies fastest) */
/* chars are numeric 0-7, could easily be ascii '0'-'7' */
/* undecided which is best. text editor could edit ascii file */
/* returns TRUE if successful, FALSE if not */
/* -------------------------------------------------------------- */
int writearea(filename)
char *filename;
{
unsigned charcount; /* limits file size to 65535, 1 fwrite chunk */
FILE *outfile;
char *p; /* 2nd pointer to area chars */
if(!(p = area->address)) /* NOTHING THERE TO write */
return(FALSE);
if(!(outfile = fopen(filename,"w")))
return(FALSE);
if(fprintf(outfile,"%d,%d\n",area->xspan,area->yspan) == ERR)
return(FALSE);
charcount = (area->xspan) * (area->yspan);
if(fwrite(p,1,charcount,outfile) != charcount)
return(FALSE);
if(fclose(outfile) == ERR)
return(FALSE);
return(TRUE);
}
/* -------------------------------------------------------------- */
/* readarea() reads graphics area from disk */
/* returns TRUE if succeed, FALSE if fail */
/* -------------------------------------------------------------- */
int readarea(filename)
char *filename;
{
int width, height;
unsigned i;
FILE *infile;
if(area->address) /* IF BUFFER ALREADY IN USE, */
free(area->address); /* FREE IT FOR REUSE */
if(!(infile = fopen(filename,"r")))
return(FALSE);
if(fscanf(infile,"%d,%d\n",&width,&height) != 2)
return(FALSE);
area->xspan = width;
area->yspan = height;
if(((long)(area->xspan)*(long)(area->yspan)) > 65535)
return(FALSE);
i = (area->xspan) * (area->yspan);
if(!(area->address = malloc(i)))
return(FALSE);
if(fread(area->address,1,i,infile) != i)
return(FALSE);
fclose(infile);
return(TRUE);
}
/* -------------------------------------------------------------- */
/* curblink() blinks the "+" cursor on/off */
/* -------------------------------------------------------------- */
void curblink(x,y)
int x, y;
{
pset(x-3,y,'x',7);
pset(x-2,y,'x',7);
pset(x-1,y,'x',7);
pset(x,y,'x',7);
pset(x+1,y,'x',7);
pset(x+2,y,'x',7);
pset(x+3,y,'x',7);
pset(x,y+3,'x',7);
pset(x,y+2,'x',7);
pset(x,y+1,'x',7);
pset(x,y-1,'x',7);
pset(x,y-2,'x',7);
pset(x,y-3,'x',7);
return;
}
/* -------------------------------------------------------------- */
/* drawhelp() displays help screen */
/* -------------------------------------------------------------- */
void drawhelp()
{
clrscr();
puts("\033m70Keypad: \033m201-4,6-9 \033m40Move Cursor \033m20. \033m40double cursor step \033m20SHIFT 0-7 \033m40En/Disable planes\n");
puts(" \033m205 \033m40Toggle Draw/Move \033m20- \033m401/2 cursor step \033m20SHIFT-8 \033m40Save SCREEN.ZED\n");
puts(" \033m20HOME \033m40center screen \033m20ARROWS \033m40move 1 dot \033m20U \033m40(Undo) Load SCREEN.ZED\n");
puts(" \033m20SHIFT-HOME \033m40top left \033m20SHIFT-ARROWS \033m40to edges \033m20# \033m40Toggle AUTOZEDSAVE\n");
puts("\n");
puts("\033m70Keyboard: \033m200-7 \033m40Set Color \033m20= \033m40GOTO new xy \033m20HELP \033m40HELP \033m20Q \033m40QUIT program\n");
puts("\033m20B \033m40BOX xy - x1y1 \033m20! \033m40AREA SAVE \033m20S \033m40STATUS of variables\n");
puts("\033m20F \033m40FILL box xy - x1y1 \033m20@ \033m40AREA PUT \033m20F2 \033m40PUT Mode \033m20ESC \033m40SAVE xy as x1y1\n");
puts("\033m20L \033m40LINE xy - x1y1 \033m20| \033m40Set Paint BORDER \033m20G \033m40GET disk file\n");
puts("\033m20C \033m40CIRCLE x1y1 to xy \033m20P \033m40PAINT area \033m20Z \033m40.ZED screen save\n");
puts("\n");
puts("\033m20F1 \033m40Save X for OVAL \033m20- \033m40Darken by 1 color \033m20N \033m40Invert a color plane\n");
puts("\033m20ESC \033m40Save Y for OVAL \033m20+ \033m40Lighten by 1 color \033m20R \033m40Rotate color planes\n");
puts("\033m20O \033m40Draw OVAL \033m20E \033m40Enhance contrast \033m20* \033m40Clear screen\n");
puts(" \033m20A \033m40Average dot colors (uses pg.2 as scratch)\n");
puts("\n");
puts("\033m20^T \033m40Change TEXT fore/back colors \033m20~ \033m40FLIP screen \033m20D \033m40Delete a file\n");
puts("\033m20T \033m40Enter TEXT to SAVE buffer\n");
puts("\033m20F3 \033m40Save graphics area to disk \033m20F4 \033m40Read graphics area from disk\n");
puts("\n");
puts("\033m30Press any key to return to edit screen...");
setcolor(7,0);
return;
}
/* -------------------------------------------------------------- */
/* SCALFIL() */
/* LOADS FILE FROM DISK, PERFORMS SCALING, ROTATION, TRANSLATION */
/* RETURNS TRUE IF SUCCEED, FALSE IF FAIL */
/* -------------------------------------------------------------- */
int scalfil(fnam,xfactor,yfactor,theta,phi,color,xtrans,ytrans,psetmode)
char *fnam; /* FILE NAME TO USE */
double xfactor, yfactor, theta, phi;
int color, xtrans, ytrans;
char psetmode;
{
char *buf, *buftwo;
double a, b, c, d;
FILE *infile;
int i, x, y, z, plotx, ploty;
char userquit = FALSE;
a = xfactor * cos(theta); /* CONVERT TO RECTANGULAR */
b = -yfactor * sin(phi); /* TRY INVERTING YFACTOR SOMETIME */
c = xfactor * sin(theta); /* BECAUSE AS IT IS, PROGRAM INTERPRETS */
d = yfactor * cos(phi); /* ANGLES BACKWARDS: + IS CLOCKWISE */
if((infile = fopen(fnam,"r")) == NULL) return(FALSE);
if(!(buf = calloc(18000,1))) return(FALSE);
for(color = 4 ; color >= 1 ; color--)
{
if(color == 3) continue; /* ONLY G, R, B */
if(fread(buf,1,18000,infile) == 0) /* READ AS MANY PLANES AS EXIST */
break; /* NO ERROR FOR SHORT FILE */
/* WILL JUST SHOW WHAT WAS THERE */
buftwo = buf; /* SET SECONDARY POINTER */
for(i = 0 ; i < 18000 ; i++) /* RUN THRU ENTIRE BUFFER */
{
if(csts() == 3) /* ALLOW USER ABORT WITH CONTROL-C */
{
ci(); /* DISCARD THE CHARACTER */
userquit = TRUE; /* SET FLAG TO BREAK OUT OF OUTER FOR LOOP */
break; /* BREAK OUT OF THIS FOR LOOP */
}
if(z = (int)(*buftwo)) /* BYPASS ALL IF NO BITS ON */
{
/* -319 AND -112 REFERENCE ENTIRE IMAGE TO SCREEN CENTER, */
/* SO ROTATION OCCURS AROUND THE CENTER. HOWEVER, */
/* THEY MUST BE RE-REFERENCED TO ACTUAL SCREEN for PSET() */
/* IF -319 AND -112 AREN'T THERE, ENTIRE SCREEN ROTATES */
/* AROUND THE UPPER LEFTHAND CORNER */
/* JUST DO THESE CALCS ONCE, FOR SPEED */
x = (i%80) * 8 - 319; /* ADDRESS OF LEFTMOST BIT */
y = (int)(i/80) - 112; /* 80 BYTES PER LINE */
/* TEST IF EACH BIT IS ON */
if(z & 128)
{
plotx=(int)(a*(double)x+b*(double)y)+xtrans;
ploty=(int)(c*(double)x+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 64)
{
plotx=(int)(a*(double)(x+1)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+1)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 32)
{
plotx=(int)(a*(double)(x+2)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+2)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 16)
{
plotx=(int)(a*(double)(x+3)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+3)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 8)
{
plotx=(int)(a*(double)(x+4)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+4)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 4)
{
plotx=(int)(a*(double)(x+5)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+5)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 2)
{
plotx=(int)(a*(double)(x+6)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+6)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
if(z & 1)
{
plotx=(int)(a*(double)(x+7)+b*(double)y)+xtrans;
ploty=(int)(c*(double)(x+7)+d*(double)y)+ytrans;
pset(plotx + 319,ploty + 112,psetmode,color);
}
} /* END IF(Z) */
buftwo++;
} /* END FOR(I=1 TO 18000) */
if(userquit) /* IF USER TYPED CONTROL-C, */
break; /* BREAK OUT OF COLORS LOOP TO RETURN FROM FUNCTION */
} /* END FOR(COLORS) */
fclose(infile);
free(buf);
return(TRUE);
}
/* -------------------------------------------------------------- */
/* avgcolor() within an area, convert each point to the */
/* average color of itself and its 4 neighboring points */
/* (was 8, but it was too slow) */
/* uses good rounding method for integer arithmetic */
/* add (number/2) to number before doing division. if original division */
/* would have left a remainder of (n/2) or greater, (.5), now when */
/* it's truncated, it will be to nearest whole number (rounded) */
/* check entire program library for places to use this */
/* could try different averaging methods: median, mode, weighted */
/* or choose highest or lowest of neighboring colors */
/* -------------------------------------------------------------- */
void avgcolor(startx,starty,endx,endy)
int startx, starty, endx, endy;
{
int h, i, j, color, count;
char autorot;
char ch = '\0';
save25th(gbuf);
erase25th();
puts("\033y5"); /* CURSOR ON */
puts("Enter repetition count: ");
scanf("%d\n",&count);
erase25th();
puts("Rotate colors an extra time between passes (y/n)? ");
autorot = tolower((char)getchar());
if(autorot == 'n') /* anything else is true */
autorot = FALSE;
erase25th();
puts("^C aborts mid-pass, ^D aborts at end of current pass. Any key...");
ci();
puts("\033x5"); /* CURSOR OFF */
put25th(gbuf);
for(h = 0 ; h < count ; h++)
{
if(!zedsave()) /* save current page */
{
puts("\007");
return; /* redo the transfer without zedsave() */
}
flipcpu(); /* flip to alt page */
zedload("SCREEN.ZED"); /* copy it to the other page */
for(j = starty ; j <= endy ; j++) /* y axis */
{
if(csts()) /* ^C quits */
{
ch = ci();
if(ch == 3)
{
flipcpu(); /* back to orig page */
return;
}
}
for(i = startx ; i <= endx ; i++) /* x axis */
{ /* average neighboring pts, offscreen = 0 */
color = MAX(pset(i,j-1,'s',0),0) + MAX(pset(i-1,j,'s',0),0) +
MAX(pset(i,j,'s',0),0) + MAX(pset(i+1,j,'s',0),0) +
MAX(pset(i,j+1,'s',0),0);
flipcpu(); /* to orig page */
pset(i,j,'p',(color+2)/5); /* set point */
flipcpu(); /* to alt page */
} /* end x axis (i) */
} /* end y axis (j) */
flipcpu(); /* to orig page */
for(i = 0 ; i < 3 ; i++)
{
rotplanes();
for(j = 0 ; j < 15000 ; j++); /* delay loop */
}
if(ch == 4) /* ^D aborts at end of pass */
break;
if(autorot)
rotplanes(); /* extra rotation to avoid pattern stagnation */
} /* end for(repetition count h) */
return;
} /* end avgcolor() */
/* -------------------------------------------------------------- */
/* dopaint() execute paint command */
/* IF paint FAILS, IT JUST LEAVES SOME UNPAINTED */
/* -------------------------------------------------------------- */
void dopaint(x,y,border)
int x, y;
char *border;
{
char ch, buf[40];
static char paintcolors[40]; /* colors to paint with */
static int tile = TRUE; /* whether to use regular paint tiling */
/* if false, random spray painting is used */
save25th(gbuf);
do
{
erase25th();
printf("Paint color(s) to use or CR for <%s>: ",paintcolors);
gets(buf);
if(strlen(buf))
strcpy(paintcolors,buf);
}
while(!strlen(paintcolors)); /* there must be something in paintcolors */
erase25th();
printf("<T>ile or <S>pray, CR for %s: ",tile?"<TILE>":"<SPRAY>");
ch = tolower((char)getchar());
if(ch == 't') tile = TRUE;
if(ch == 's') tile = FALSE;
put25th(gbuf);
if(!paint(x,y,paintcolors,border,tile))
put25th(gbuf); /* overwrite paint's error msg */
return;
}
/* -------------------------------------------------------------- */
/* adjustcolor() handles contrast commands: +, -, enhance */
/* -------------------------------------------------------------- */
void adjustcolor(x,y,x1,y1,mode)
int x, y, x1, y1;
char mode;
{
int i, j, colorfound;
switch(mode)
{
case '-': /* REDUCE ALL COLORS BY 1 */
case '+': /* INCREASE ALL COLORS BY 1 */
for(j = y1 ; j <= y ; j++)
{
for(i = x1 ; i <= x ; i++)
pset(i,j,mode,0);
if(csts() == 3)
break;
}
break;
case 'e': /* ENHANCE CONTRAST */
for(j = y1 ; j <= y ; j++)
{
for(i = x1 ; i <= x ; i++)
{
colorfound = pset(i,j,'s',0);
if(colorfound > 4) /* LEAVES GREEN UNCHANGED */
pset(i,j,'p',MIN(colorfound+1,7));
else
if(colorfound < 4)
pset(i,j,'p',MAX(colorfound-1,0));
}
if(csts() == 3)
break;
}
break;
} /* end switch */
return;
} /* end adjustcolor() */
/* -------------------------------------------------------------- */
/* setsteps() adjust xstep, ystep */
/* -------------------------------------------------------------- */
void setsteps(xstep,ystep)
int *xstep, *ystep;
{
char buf[20];
save25th(gbuf);
erase25th();
puts("Enter XSTEP YSTEP or CR for no change: ");
gets(buf);
if(strlen(buf))
{
sscanf(buf,"%d %d",xstep,ystep); /* already pointers */
*ystep = (int)((double)(*ystep) * 0.48046875 + .5);
}
put25th(gbuf);
return;
}
/* -------------------------------------------------------------- */
/* main() */
/* -------------------------------------------------------------- */
main(argc,argv)
int argc;
char **argv;
{
char vidportinit; /* SETTING FOUND AT VIDEO PORT */
char ch, ch2; /* USER INPUT */
char fnam[51]; /* HOLDS FILE NAMES */
int foreground, background; /* FOREGROUND, BACKGROUND TEXT COLORS */
char border[9]; /* BORDER string FOR PAINT COMMAND */
int i, j, k; /* COUNTERS */
int x, y; /* COORDINATES OF CURRENT CURSOR LOCATION */
int x1, y1; /* 1ST SET OF SAVED COORDINATES */
int x2, y2; /* USED FOR DRAWING AFTER CURSOR MOVEMENT */
int x3, y3; /* USED FOR DRAWING OVALS */
int draw = FALSE; /* MODE: CURSOR DRAWS OR JUST MOVES */
int autosave = FALSE; /* WHETHER TO ZEDSAVE() BEFORE EACH ACTION */
/* (ALLOWS ANY CHANGE TO BE UNDONE) */
int helpthere; /* WHETHER HELP SCREEN IS IN PAGE 2 */
/* ONLY SET FALSE BY A FILE LOAD */
/* IF USER ERASES SCREEN, MUST LOAD FOR HELP */
int flip; /* WHETHER TO USE PAGE 2 FOR LOADED IMAGE */
int xstep, ystep; /* DISTANCE CURSOR MOVES WITH EACH STEP */
int color = 4; /* COLOR TO DRAW IN */
char psetmode = 'p'; /* X,P,O = PSET MODE */
char drawdir = 0; /* keypad key of direction of last draw command */
int radius; /* RADIUS OF CIRCLE TO DRAW */
int xtrans, ytrans; /* COORDINATE TRANSLATIONS WHEN LOADING FILE */
double xrange, yrange; /* USED IN DRAWING CIRCLE */
double xfactor, yfactor; /* SCALING FACTORS */
double theta, phi; /* ANGLES USED IN ROTATING IMAGE */
freeall(1024);
puts("\033z\033x1\033x5"); /* RESET TERM., ENABLE 25TH LINE, CURSOR OFF */
puts("\033y?"); /* DISABLE H100 KEY EXPANSION */
_outb(0x78,0xD8); /* ENABLE ALL PLANES */
flipcpu(); /* GO TO PAGE 2 */
flipcrtc();
drawhelp(); /* CREATE HELP SCREEN */
/* CI(); */ /* THIS IS THE 'ANY CHAR' DRAWHELP ASKS FOR */
/* OMIT ON PGM ENTRY */
flipcpu(); /* RETURN TO PAGE 1 */
flipcrtc();
helpthere = TRUE;
locate(25,1);
x1 = y1 = x2 = y2 = x3 = y3 = 0;
strcpy(border,"0"); /* initial border is black */
area->address = 0; /* INDICATES THAT IT HASN'T BEEN USED YET */
xstep = 16;
ystep = (int)((double)xstep * 0.48046875 + .5);
x = 320;
y = 112;
if(argc > 1) /* EDIT FILENAME NOW ALLOWED */
zedload(argv[1]);
curblink(x,y);
while(1) /* DO FOREVER. EXIT IS IN LOOP */
{
ch = tolower(ci()); /* wait for user input */
curblink(x,y); /* turn cursor off during any activity */
if((autosave) && (ch != 'u')) /* ALLOW LAST ACTION TO BE UNDONE */
if(!zedsave())
puts("\007"); /* WARN OF FAILURE */
switch(ch)
{
/* UNSHIFTED KEYPAD NUMBERS USED AS ARROW KEYS */
case(KEY8): /* 8 up */
case 18: /* ^R */
x2 = x; y2 = y;
y -= ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 2) /* direction reversed, */
pset(x2,y2,'x',color); /* so set previous endpoint */
line(x,y+1,x2,y2,psetmode,color);
}
drawdir = 8;
break;
case(KEY2): /* 2 down */
case 3: /* ^C */
x2 = x; y2 = y;
y += ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 8)
pset(x2,y2,'x',color);
line(x,y-1,x2,y2,psetmode,color);
}
drawdir = 2;
break;
case(KEY6): /* 6 right */
case 6: /* ^F */
x2 = x; y2 = y;
x += xstep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 4)
pset(x2,y2,'x',color);
line(x-1,y,x2,y2,psetmode,color);
}
drawdir = 6;
break;
case(KEY4): /* 4 left */
case 1: /* ^A */
x2 = x; y2 = y;
x -= xstep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 6)
pset(x2,y2,'x',color);
line(x+1,y,x2,y2,psetmode,color);
}
drawdir = 4;
break;
case(KEY3): /* 3 down-right */
x2 = x; y2 = y;
x += xstep;
y += ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 7)
pset(x2,y2,'x',color);
line(x,y,x2,y2,psetmode,color);
}
drawdir = 3;
break;
case(KEY7): /* 7 up-left */
x2 = x; y2 = y;
x -= xstep;
y -= ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 3)
pset(x2,y2,'x',color);
line(x,y,x2,y2,psetmode,color);
}
drawdir = 7;
break;
case(KEY9): /* 9 up-right */
x2 = x; y2 = y;
x += xstep;
y -= ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 1)
pset(x2,y2,'x',color);
line(x,y,x2,y2,psetmode,color);
}
drawdir = 9;
break;
case(KEY1): /* 1 down-left */
x2 = x; y2 = y;
x -= xstep;
y += ystep;
if(draw)
{
if(psetmode == 'x')
if(drawdir == 9)
pset(x2,y2,'x',color);
line(x,y,x2,y2,psetmode,color);
}
drawdir = 1;
break;
/* ACTUAL ARROW KEYS STEP 1 DOT */
/* BUT DON'T CHANGE THE STEP */
case(UP): /* UP */
case 5: /* ^E */
x2 = x; y2 = y;
y--;
if(draw)
pset(x,y,psetmode,color);
break;
case(DOWN): /* DOWN */
case 24: /* ^X */
x2 = x; y2 = y;
y++;
if(draw)
pset(x,y,psetmode,color);
break;
case(RIGHT): /* RIGHT */
case 4: /* ^D */
x2 = x; y2 = y;
x++;
if(draw)
pset(x,y,psetmode,color);
break;
case(LEFT): /* LEFT */
case 19: /* ^S */
x2 = x; y2 = y;
x--;
if(draw)
pset(x,y,psetmode,color);
break;
/* SHIFT ARROWS MOVE TO SCREEN EDGES */
case(SHIFTLEFT): /* CENTER LEFT EDGE */
x2 = x; y2 = y;
x = 0; y = 112;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case(SHIFTRIGHT): /* CENTER RIGHT EDGE */
x2 = x; y2 = y;
x = 639; y = 112;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case(SHIFTDOWN): /* CENTER BOTTOM EDGE */
x2 = x; y2 = y;
x = 320; y = 224;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case(SHIFTUP): /* CENTER TOP EDGE */
x2 = x; y2 = y;
x = 320; y = 0;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case(KEYPERIOD): /* KEYPAD PERIOD DOUBLES STEPs */
xstep *= 2;
ystep *= 2;
break;
case(KEYMINUS): /* KEYPAD MINUS HALVES STEPs */
xstep = MAX(((xstep + 1)/ 2),1); /* rounded */
ystep = MAX(((ystep + 1)/ 2),1);
break;
case(ENTER): /* specify xstep, ystep */
setsteps(&xstep,&ystep);
break;
case(HOME): /* HOME GOES TO CENTER OF SCREEN */
x2 = x; y2 = y;
x = 320; y = 112;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case(SHIFTHOME): /* SHIFT HOME GOES TO TOP LEFT CORNER */
x2 = x; y2 = y;
x = 0; y = 0;
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
/* KEYBOARD NUMBERS CHANGE COLOR */
case '0': color = 0; break; /* 0 = BLACK */
case '1': color = 1; break; /* 1 = BLUE */
case '2': color = 2; break; /* 2 = RED */
case '3': color = 3; break; /* 3 = MAGENTA */
case '4': color = 4; break; /* 4 = GREEN */
case '5': color = 5; break; /* 5 = CYAN */
case '6': color = 6; break; /* 6 = YELLOW */
case '7': color = 7; break; /* 7 = WHITE */
/* maybe change this to goto x1,y1 */
case '=': /* = EQUALS MEANS GOTO X Y */
save25th(gbuf); /* seldom used */
x2 = x; y2 = y;
do
{
erase25th();
puts("GOTO: Enter x y (space between): ");
}
while(scanf("%d %d",&x,&y) != 2);
put25th(gbuf);
if(draw)
line(x,y,x2,y2,psetmode,color);
break;
case 27: /* ESCAPE: SAVE COORDINATES */
x1 = x;
y1 = y;
break;
case 'l': /* DRAW LINE FROM SAVED POINT */
line(x1,y1,x,y,psetmode,color); /* TO CURSOR */
break;
case 'b': /* DRAW A BOX */
box(x1,y1,x,y,psetmode,color,FALSE);
break;
case 'f': /* FILL A BOX WITH A COLOR */
box(x1,y1,x,y,psetmode,color,TRUE);
break;
case 'c': /* DRAW CIRCLE CENTERED AT CURSOR */
/* xpset mode should only be used */
/* for testing. then use pset */
xrange = (double)(x1 - x); /* RADIUS = CURSOR TO X1Y1 */
yrange = 2.081300813 * (double)(y1 - y); /* 1./.48046875 */
radius = (int)(sqrt(xrange * xrange + yrange * yrange) + .5);
circle(x,y,radius,psetmode,color,1.);
break;
case 'o': /* DRAW OVAL */
xrange = (double)(abs(x3 - x));
yrange = (double)(abs(y1 - y));
if(xrange >= yrange)
{ /* HORIZ. MEAS. IN X UNITS */
yrange *= 2.081300813;
circle(x,y,(int)xrange,psetmode,color,(yrange/xrange));
}
else
{ /* VERT. MEAS. IN Y UNITS */
xrange /= 2.081300813;
circle(x,y,(int)yrange,psetmode,color,(yrange/xrange));
}
break;
case(F1): /* FUNCTION KEY F1 SAVES X3Y3 */
x3 = x; y3 = y;
break;
case(KEY5): /* KEYPAD 5 TOGGLES DRAW/MOVE MODE */
draw = !draw;
break;
case '#': /* POUND SIGN TOGGLES AUTOSAVE */
autosave = !autosave;
break;
case(HELP): /* HELP KEY DISPLAYS HELP PAGE */
vidportinit = _inb(0xD8); /* SAVE PORT SETTING */
_outb(0x78,0xD8); /* ENABLE ALL PLANES */
restorepg1(); /* MAKE SURE WE START ON PAGE 1 */
flipcpu();
flipcrtc(); /* GO TO PAGE 2 */
if(!helpthere)
{
drawhelp(); /* IF ERASED, REDRAW SCREEN */
helpthere = TRUE;
}
ci();
flipcpu(); /* RETURN TO PAGE 1 */
flipcrtc();
_outb(vidportinit,0xD8); /* RESTORE VIDEO PORT */
break;
case 's': /* STATUS DISPLAY */
save25th(gbuf);
erase25th();
printf("x%d y%d x1=%d y1=%d mode:%c pset:%c save:%c c:%d bor:%s steps:x%d y%d",
x,y,x1,y1,(draw?'D':'M'),toupper(psetmode),(autosave?'T':'F'),
color,border,xstep,ystep);
ci();
put25th(gbuf);
break;
case(SHIFTKEY8): /* SHIFT-KEYPAD 8 AUTO-ZEDSAVE */
if(!zedsave()) puts("\007");
break;
case 'z': /* ZEDSAVE */
do
{
save25th(gbuf); /* SAVE GRAPHICS */
erase25th(); /* ERASE & LOCATE */
inpnam(fnam); /* GET FILE NAME */
put25th(gbuf); /* RESTORE GRAPHICS */
if(!zedsave()) puts("\007"); /* SAVE THE SCREEN */
} /* FILE NAME EXISTS? (but abort if user enters CR) */
while((strlen(fnam)) && (rename("SCREEN.ZED",fnam) == ERR));
break;
case 'g': /* GET A DISK FILE */
save25th(gbuf); /* SAVE LINE 25 GRAPHICS */
flip = FALSE;
erase25th();
inpnam(fnam); /* GET FILE NAME */
if(!strlen(fnam)) /* nothing entered, abort */
{
put25th(gbuf);
break;
}
erase25th();
puts("\033y5Use Page 2? y/n: ");
if(tolower(ci()) == 'y')
flip = TRUE;
erase25th(); /* CLEAR & REPOS. CURSOR */
puts("ZEDLOAD? (overwrites current screen) y/n: ");
if(tolower(ci()) == 'y')
{
erase25th();
puts("\033x5"); /* CURSOR OFF */
put25th(gbuf); /* RESTORE GRAPHICS */
if(flip)
{
restorepg1(); /* MAKE SURE WE'RE ON PAGE 1 */
flipcpu(); /* FLIP TO PAGE 2 */
flipcrtc(); /* FLIP */
helpthere = FALSE; /* HELP HAS BEEN ERASED */
}
zedload(fnam);
if(flip)
{
flipcpu(); /* RETURN TO PAGE 1 */
flipcrtc();
}
break;
}
erase25th();
puts("Enter X and Y rotation (space between): ");
scanf("%lf %lf",&theta,&phi);
theta *= .0174532925; /* TO RADIANS, NEG. IS OK */
phi *= .0174532925;
erase25th();
puts("Enter X and Y scaling factors (space between): ");
scanf("%lf %lf",&xfactor,&yfactor);
erase25th();
puts("Enter X and Y translation factors (space between): ");
scanf("%d %d",&xtrans,&ytrans);
put25th(gbuf);
locate(25,1); /* REST CURSOR AT BOTTOM */
puts("\033x5"); /* AND TURN IT OFF */
if(flip)
{
restorepg1(); /* MAKE SURE WE'RE ON PAGE 1 */
flipcpu(); /* FLIP */
flipcrtc(); /* FLIP */
helpthere = FALSE; /* HELP HAS BEEN ERASED */
}
if(!scalfil(fnam,xfactor,yfactor,theta,phi,color,xtrans,ytrans,psetmode))
{
save25th(gbuf);
erase25th();
puts("File not found or Not enough memory (free up the get-area)");
ci();
put25th(gbuf);
}
if(flip)
{
flipcpu(); /* RETURN TO PAGE 1 */
flipcrtc();
}
break;
case 'u': /* "UNDO" WORKS ONLY IF */
zedload("SCREEN.ZED"); /* SHIFT-KEYPAD 8 IS USED */
break; /* FREQUENTLY */
case '!': /* AREA SAVE */
if(!getarea(x1,y1,x,y))
{
save25th(gbuf);
erase25th();
puts("\007SAVE FAILED - X or Y off screen or area too big");
ci();
put25th(gbuf);
}
break;
case '@': /* AREA PUT */
putarea(x,y,psetmode);
break;
case(0x14): /* CONTROL-T CHANGE TEXT COLOR */
save25th(gbuf);
do
{
erase25th();
puts("Enter foreground background (space between): ");
}
while(scanf("%d %d",&foreground,&background) != 2);
setcolor(foreground,background);
put25th(gbuf);
break;
case 't': /* ENTER TEXT LINE */
save25th(gbuf); /* AND AUTOMATICALLY */
erase25th(); /* SAVE TO STORED AREA */
gets(fnam);
getarea(0,216,639,224); /* SAVE LAST 9 SCAN LINES */
put25th(gbuf);
break;
case '|': /* SET PAINT BORDER */
save25th(gbuf);
erase25th();
puts("Enter border color(s) 0-7 or E(xcept), no spaces: ");
scanf("%s",border);
border[8] = '\0'; /* terminate in case too long */
strlwr(border); /* won't affect digits */
put25th(gbuf); /* must put before reading point! */
if(index(border,'e')) /* all colors are border */
{ /* except color at cursor loc. */
k = 0;
i = pset(x,y,'s',0);
for(j = 0 ; j < 8 ; j++)
if(j != i)
border[k++] = (char)(j + 0x30);
border[k] = '\0';
}
break;
case 'p': /* PAINT AN AREA (only works in pset mode) */
dopaint(x,y,border);
break;
case '*': /* CLEAR SCREEN */
clrscr();
break;
/* SHIFTED KEYPAD NUMBERS */
/* THESE DON'T CHANGE DATA, ONLY DISABLE PLANES */
/* AND PLANES CAN BE WRITTEN TO WHILE OFF */
case(SHIFTKEY0): _outb(0x7F,0xD8); break; /* BLACK (BLANK SCREEN) */
case(SHIFTKEY1): _outb(0x7B,0xD8); break; /* BLUE */
case(SHIFTKEY2): _outb(0x7E,0xD8); break; /* RED */
case(SHIFTKEY3): _outb(0x7A,0xD8); break; /* BLUE,RED,MAGENTA */
case(SHIFTKEY4): _outb(0x7D,0xD8); break; /* GREEN */
case(SHIFTKEY5): _outb(0x79,0xD8); break; /* GREEN,BLUE,CYAN */
case(SHIFTKEY6): _outb(0x7C,0xD8); break; /* RED,GREEN,YELLOW */
case(SHIFTKEY7): _outb(0x78,0xD8); break; /* WHITE:ALL ENABLED */
case 'n': /* CREATE NEGATIVE IMAGE */
save25th(gbuf); /* OF 1 or all PLANEs */
erase25th();
puts("Invert <B>lue, <R>ed, <G>reen, or <A>ll? ");
ch2 = ci();
put25th(gbuf);
negplane(ch2);
break;
case '-': /* DARKEN IMAGE */
case '+': /* LIGHTEN IMAGE */
case 'e': /* enhance contrast */
adjustcolor(x,y,x1,y1,ch);
break;
case 'r': /* ROTATE COLOR PLANES */
rotplanes();
break;
case '~': /* TILDE FLIPS BETWEEN PAGES 1 AND 2 */
flipcpu();
flipcrtc();
break;
case 'q':
save25th(gbuf);
erase25th();
puts("Press ESC to exit "); /* ALLOW USER TO CHANGE MIND */
ch2 = ci();
if(ch2 == 27)
{
restorepg1();
puts("\033z");
exit(0);
}
put25th(gbuf);
break;
case 'd': /* DELETE A FILE TO MAKE ROOM */
save25th(gbuf); /* ZEDSAVE FAILS IF DISK FULL */
erase25th();
puts("\033y5"); /* CURSOR ON */
puts("File to delete: ");
gets(fnam);
unlink(fnam);
puts("\033x5"); /* CURSOR OFF */
put25th(gbuf);
break;
case(F2): /* CHANGE X,O,P PUTAREA() MODE */
save25th(gbuf);
erase25th();
do
{
puts("PUT mode (p,x,o): ");
psetmode = tolower((char)getchar());
}
while(!index("xpo",psetmode));
put25th(gbuf);
break;
case(F3): /* save graphics area to disk */
save25th(gbuf);
erase25th();
puts("\033y5"); /* CURSOR ON */
puts("Filename for area save (c/r aborts): ");
gets(fnam);
puts("\033x5"); /* CURSOR OFF */
if(strlen(fnam))
writearea(fnam);
put25th(gbuf);
break;
case(F4): /* read graphics area from disk */
save25th(gbuf);
erase25th();
puts("\033y5"); /* CURSOR ON */
puts("Filename containing graphics area (c/r aborts): ");
gets(fnam);
puts("\033x5"); /* CURSOR OFF */
if(strlen(fnam))
readarea(fnam);
put25th(gbuf);
break;
case 'a': /* average color values */
avgcolor(x1,y1,x,y);
break;
default:
break;
} /* END SWITCH */
curblink(x,y); /* cursor back on while idle */
} /* END WHILE */
} /* END MAIN */
|
|
|
|
|
Copyright ©2011 Steven Whitney. Last modified Tue 05/24/2011 12:28:39 -0700. |
||