qt-agistudio/src/picture.cpp

1561 lines
35 KiB
C++

/*
* QT AGI Studio :: Copyright (C) 2000 Helen Zommer
*
* A big part of this code is adapted from the original (MSDOS)
* Picedit developed by Lance Ewing.
*
* This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include "linklist.h"
#include "picture.h"
#include "menu.h"
static bool picDrawEnabled0,priDrawEnabled0;
static int picColour0,priColour0;
//*********************************************************
Picture::Picture()
{
picPos=picStart=picLast=NULL;
this->freeList();
bg_on = false;
}
//*********************************************************
char *Picture::showPos(int *code,int *val)
//show current picture buffer position
{
char tmp1[6];
tmp[0]=0;
struct picCodeNode *temp;
int count;
if(picPos){
*code=picPos->node;
if(picPos->next){
*val=picPos->next->node;
}
}
for (temp=picPos, count=0; ((count<6)&&(temp!=NULL)); count++, temp=temp->next) {
sprintf(tmp1, "%02X", temp->node);
strcat(tmp,tmp1);
}
return tmp;
}
//*********************************************************
int Picture::setBufPos(int inputValue)
//set current picture buffer position to inputValue
{
if ((inputValue < 0) || (inputValue > bufLen)) return 1;
if (inputValue == bufLen) {
picPos = NULL;
bufPos = inputValue;
}
else{
/* Find given location */
while (bufPos != inputValue) {
if (inputValue < bufPos) {
if (picPos == NULL)
picPos = picLast;
else
picPos = picPos->prior;
bufPos--;
}
else {
picPos = picPos->next;
bufPos++;
}
}
/* Find current action position */
while (picPos->node < 0xF0) {
picPos = picPos->prior;
bufPos--;
}
}
draw();
init_tool();
return 0;
}
//*********************************************************
void Picture::qstore(byte q)
{
if (spos+1==rpos || (spos+1==QUMAX && !rpos)) {
return;
}
buf[spos] = q;
spos++;
if (spos==QUMAX) spos = 0; /* loop back */
}
//*********************************************************
byte Picture::qretrieve()
{
if (rpos==QUMAX) rpos=0; /* loop back */
if (rpos==spos) {
return EMPTY;
}
rpos++;
return buf[rpos-1];
}
/**************************************************************************
** getCode
**
** Gets the next picture code from the linked list.
**************************************************************************/
byte Picture::getCode(struct picCodeNode **temp)
{
byte retVal;
if (*temp == NULL) return 0xFF;
retVal = (*temp)->node;
*temp = (*temp)->next;
return retVal;
}
byte Picture::testCode(struct picCodeNode **temp)
{
byte retVal;
if (*temp == NULL) return 0xFF;
retVal = (*temp)->node;
return retVal;
}
/**************************************************************************
** picPSet
**
** Draws a pixel in the picture screen.
**************************************************************************/
void Picture::picPSet(word x, word y)
{
x<<=1;
if (x >= MAX_W) return;
if (y >= MAX_H) return;
picture[y*MAX_W+x] = picColour;
picture[y*MAX_W+x+1] = picColour;
}
/**************************************************************************
** priPSet
**
** Draws a pixel in the priority screen.
**************************************************************************/
void Picture::priPSet(word x, word y)
{
x<<=1;
if (x >= MAX_W) return;
if (y >= MAX_H) return;
priority[y*MAX_W+x] = priColour;
priority[y*MAX_W+x+1] = priColour;
}
/**************************************************************************
** pset
**
** Draws a pixel in each screen depending on whether drawing in that
** screen is enabled or not.
**************************************************************************/
void Picture::pset(word x, word y)
{
if (picDrawEnabled) picPSet(x, y);
if (priDrawEnabled) priPSet(x, y);
}
/**************************************************************************
** picGetPixel
**
** Get colour at x,y on the picture page.
**************************************************************************/
byte Picture::picGetPixel(word x, word y)
{
x <<= 1;
if (x >= MAX_W) return 4;
if (y >= MAX_H) return 4;
return (picture[y*MAX_W+x]);
}
/**************************************************************************
** priGetPixel
**
** Get colour at x,y on the priority page.
**************************************************************************/
byte Picture::priGetPixel(word x, word y)
{
x <<= 1;
if (x >= MAX_W) return 4;
if (y >= MAX_H) return 4;
return (priority[y*MAX_W+x]);
}
/**************************************************************************
** round
**
** Rounds a float to the closest int. Takes into actions which direction
** the current line is being drawn when it has a 50:50 decision about
** where to put a pixel.
**************************************************************************/
int Picture::round(float aNumber, float dirn)
{
if (dirn < 0)
return ((aNumber - floor(aNumber) <= 0.501)? (int)floor(aNumber) : (int)ceil(aNumber));
return ((aNumber - floor(aNumber) < 0.499)? (int)floor(aNumber) : (int)ceil(aNumber));
}
/**************************************************************************
** drawline
**
** Draws an AGI line.
**************************************************************************/
void Picture::drawline(word x1, word y1, word x2, word y2)
{
int height, width;
float x, y, addX, addY;
height = (y2 - y1);
width = (x2 - x1);
addX = (height==0?height:(float)width/abs(height));
addY = (width==0?width:(float)height/abs(width));
if (abs(width) > abs(height)) {
y = y1;
addX = (width == 0? 0 : (width/abs(width)));
for (x=x1; x!=x2; x+=addX) {
pset(round(x, addX), round(y, addY));
y+=addY;
}
pset(x2,y2);
}
else {
x = x1;
addY = (height == 0? 0 : (height/abs(height)));
for (y=y1; y!=y2; y+=addY) {
pset(round(x, addX), round(y, addY));
x+=addX;
}
pset(x2,y2);
}
}
/**************************************************************************
** okToFill
**************************************************************************/
bool Picture::okToFill(byte x, byte y)
{
if (!picDrawEnabled && !priDrawEnabled) return false;
if (picColour == 15) return false;
if (!priDrawEnabled) return (picGetPixel(x, y) == 15);
if (priDrawEnabled && !picDrawEnabled) return (priGetPixel(x, y) == 4);
return (picGetPixel(x, y) == 15);
}
/**************************************************************************
** agiFill
**************************************************************************/
void Picture::agiFill(word x, word y)
{
byte x1, y1;
rpos = spos = 0;
qstore(x);
qstore(y);
for (;;) {
x1 = qretrieve();
y1 = qretrieve();
if ((x1 == EMPTY) || (y1 == EMPTY))
break;
else {
if (okToFill(x1,y1)) {
pset(x1, y1);
if (okToFill(x1, y1-1) && (y1!=0)) {
qstore(x1);
qstore(y1-1);
}
if (okToFill(x1-1, y1) && (x1!=0)) {
qstore(x1-1);
qstore(y1);
}
if (okToFill(x1+1, y1) && (x1!=159)) {
qstore(x1+1);
qstore(y1);
}
if (okToFill(x1, y1+1) && (y1!=167)) {
qstore(x1);
qstore(y1+1);
}
}
}
}
}
/**************************************************************************
** xCorner
**
** Draws an xCorner (drawing action 0xF5)
**************************************************************************/
void Picture::xCorner(struct picCodeNode **temp)
{
byte x1, x2, y1, y2;
x1 = getCode(temp);
y1 = getCode(temp);
pset(x1,y1);
for (;;) {
x2 = getCode(temp);
if (x2 >= 0xF0) break;
drawline(x1, y1, x2, y1);
x1 = x2;
y2 = getCode(temp);
if (y2 >= 0xF0) break;
drawline(x1, y1, x1, y2);
y1 = y2;
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
/**************************************************************************
** yCorner
**
** Draws an yCorner (drawing action 0xF4)
**************************************************************************/
void Picture::yCorner(struct picCodeNode **temp)
{
byte x1, x2, y1, y2;
x1 = getCode(temp);
y1 = getCode(temp);
pset(x1, y1);
for (;;) {
y2 = getCode(temp);
if (y2 >= 0xF0) break;
drawline(x1, y1, x1, y2);
y1 = y2;
x2 = getCode(temp);
if (x2 >= 0xF0) break;
drawline(x1, y1, x2, y1);
x1 = x2;
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
/**************************************************************************
** relativeDraw
**
** Draws short lines relative to last position. (drawing action 0xF7)
**************************************************************************/
void Picture::relativeDraw(struct picCodeNode **temp)
{
byte x1, y1, disp;
char dx, dy;
x1 = getCode(temp);
y1 = getCode(temp);
pset(x1, y1);
for (;;) {
disp = getCode(temp);
if (disp >= 0xF0) break;
dx = ((disp & 0xF0) >> 4) & 0x0F;
dy = (disp & 0x0F);
if (dx & 0x08) dx = (-1)*(dx & 0x07);
if (dy & 0x08) dy = (-1)*(dy & 0x07);
drawline(x1, y1, x1 + dx, y1 + dy);
x1 += dx;
y1 += dy;
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
/**************************************************************************
** fill
**
** Agi flood fill. (drawing action 0xF8)
**************************************************************************/
void Picture::fill(struct picCodeNode **temp)
{
byte x1, y1;
for (;;) {
if ((x1 = getCode(temp)) >= 0xF0) break;
if ((y1 = getCode(temp)) >= 0xF0) break;
agiFill(x1, y1);
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
/**************************************************************************
** absoluteLine
**
** Draws long lines to actual locations (cf. relative) (drawing action 0xF6)
**************************************************************************/
void Picture::absoluteLine(struct picCodeNode **temp)
{
byte x1, y1, x2, y2;
x1 = getCode(temp);
y1 = getCode(temp);
pset(x1, y1);
for (;;) {
if ((x2 = getCode(temp)) >= 0xF0) break;
if ((y2 = getCode(temp)) >= 0xF0) break;
drawline(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
#define plotPatternPoint() \
if (patCode & 0x20) { \
if ((splatterMap[bitPos>>3] >> (7-(bitPos&7))) & 1) pset(x1, y1); \
bitPos++; \
if (bitPos == 0xff) bitPos=0; \
} else pset(x1, y1)
/**************************************************************************
** plotPattern
**
** Draws pixels, circles, squares, or splatter brush patterns depending
** on the pattern code.
**************************************************************************/
void Picture::plotPattern(byte x, byte y)
{
static unsigned char circles[][15] = { /* agi circle bitmaps */
{0x80},
{0xfc},
{0x5f, 0xf4},
{0x66, 0xff, 0xf6, 0x60},
{0x23, 0xbf, 0xff, 0xff, 0xee, 0x20},
{0x31, 0xe7, 0x9e, 0xff, 0xff, 0xde, 0x79, 0xe3, 0x00},
{0x38, 0xf9, 0xf3, 0xef, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xf3, 0xe3, 0x80},
{0x18, 0x3c, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x7e,
0x7e, 0x3c, 0x18}
};
static byte splatterMap[32] = { /* splatter brush bitmaps */
0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2,
0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14,
0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10,
0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04
};
static byte splatterStart[128] = { /* starting bit position */
0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48,
0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d,
0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf,
0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1,
0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce,
0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed,
0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6,
0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51,
0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7,
0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf,
0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0,
0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49,
0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2,
0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3,
0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1
};
int circlePos = 0;
byte x1, y1, penSize, bitPos = splatterStart[patNum];
penSize = (patCode&7);
if (x<((penSize/2)+1)) x=((penSize/2)+1);
else if (x>160-((penSize/2)+1)) x=160-((penSize/2)+1);
if (y<penSize) y = penSize;
else if (y>=168-penSize) y=167-penSize;
for (y1=y-penSize; y1<=y+penSize; y1++) {
for (x1=x-((int)ceil((float)penSize/2)); x1<=x+((int)floor((float)penSize/2)); x1++) {
if (patCode & 0x10) { /* Square */
plotPatternPoint();
}
else { /* Circle */
if ((circles[patCode&7][circlePos>>3] >> (7-(circlePos&7)))&1) {
plotPatternPoint();
}
circlePos++;
}
}
}
}
/**************************************************************************
** plotBrush
**
** Plots points and various brush patterns.
**************************************************************************/
void Picture::plotBrush(struct picCodeNode **temp)
{
byte x1, y1;
for (;;) {
if (patCode & 0x20) {
if ((patNum = getCode(temp)) >= 0xF0) break;
patNum = (patNum >> 1 & 0x7f);
}
if ((x1 = getCode(temp)) >= 0xF0) break;
if ((y1 = getCode(temp)) >= 0xF0) break;
plotPattern(x1, y1);
}
if (*temp == NULL) *temp = NULL;
else *temp = (*temp)->prior;
}
//********************************************************************
void Picture::load(byte *picdata,int picsize)
{
byte nodeData;
struct picCodeNode *temp=NULL;
bool stillLoading=true;
//init link list
picPos = picStart = picLast = NULL;
addMode = INS_MODE;
bufPos = bufLen = 0;
do {
nodeData = *picdata++;
picsize--;
if (nodeData != 0xFF) {
temp = (struct picCodeNode *)malloc(sizeof(picCodes));
if (temp == NULL) {
printf("Error allocating memory while loading picture.\n");
exit(1);
}
/* was in the original code but doesn't work
if (isFirst) {
picStart = temp;
isFirst = false;
}
*/
temp->node = nodeData;
dlstore(temp);
}
else
stillLoading = false;
} while(picsize && stillLoading);
picLast = temp;
picPos = NULL;
}
//*************************************************
int Picture::open(char *filename)
{
FILE *fptr = fopen(filename,"rb");
if(fptr==NULL){
menu->errmes("Can't open file %s ! ",filename);
return 1;
}
struct stat buf;
fstat(fileno(fptr),&buf);
ResourceData.Size=buf.st_size;
fread(ResourceData.Data,ResourceData.Size,1,fptr);
fclose(fptr);
load(ResourceData.Data,ResourceData.Size);
refill_pic = refill_pri = false;
draw();
init();
return 0;
}
//*************************************************
int Picture::open(int ResNum)
{
int err = game->ReadResource(PICTURE,ResNum);
if(!err){
load(ResourceData.Data,ResourceData.Size);
refill_pic = refill_pri = false;
draw();
init();
}
return err;
}
//*************************************************
int Picture::save(int ResNum)
{
save();
return(game->AddResource(PICTURE,ResNum));
}
//*************************************************
int Picture::save(char *filename)
{
FILE *fptr = fopen(filename,"wb");
if(fptr==NULL){
menu->errmes("Can't open file %s ! ",filename);
return 1;
}
save();
fwrite(ResourceData.Data,ResourceData.Size,1,fptr);
fclose(fptr);
return 0;
}
//*************************************************
void Picture::save()
{
byte *ptr=ResourceData.Data;
struct picCodeNode *temp;
if (picStart == NULL) { /* Black picture */
*ptr = 0xFF; /* End of picture marker */
ResourceData.Size=1;
return ;
}
temp = picStart;
*ptr++ = temp->node;
do {
temp = temp->next;
*ptr++ = temp->node;
} while (temp->next != NULL);
*ptr++ = 0xFF; /* End of picture marker */
ResourceData.Size = (int)(ptr-ResourceData.Data);
}
//*************************************************
void Picture::refill( struct picCodeNode *temp_fill_start, struct picCodeNode *temp_fill_end,int refmode)
{
struct picCodeNode *temp,*picPos0,*temp_pic=0,*temp_pri=0;
int col_pic_orig,col_pri_orig,col_pic_new,col_pri_new;
bool picDrawEnabled_orig,priDrawEnabled_orig;
bool draw_pic_orig,draw_pri_orig,draw_pic_new,draw_pri_new;
picDrawEnabled_orig=priDrawEnabled_orig=false;
col_pic_orig = col_pri_orig = -1;
temp=temp_fill_start;
draw_pic_orig=draw_pri_orig=false;
do{
temp = temp->prior;
if(temp==NULL)break;
switch(temp->node){
case 0xf0:
if(col_pic_orig==-1){
col_pic_orig = (temp->next)->node;
picDrawEnabled_orig=true;
temp_pic=temp;
}
break;
case 0xf1:
if(col_pic_orig==-1){
col_pic_orig=-2;
temp_pic=temp;
}
break;
case 0xf2:
if(col_pri_orig==-1){
col_pri_orig = (temp->next)->node;
priDrawEnabled_orig=true;
temp_pri=temp;
}
break;
case 0xf3:
if(col_pri_orig==-1){
col_pri_orig=-2;
temp_pri=temp;
}
break;
default:
if(temp->node >= 0xf4 && temp->node < 0xff){
if(col_pic_orig==-1)draw_pic_orig=true;
if(col_pri_orig==-1)draw_pri_orig=true;
}
break;
}
}while(col_pic_orig==-1 || col_pri_orig==-1); //find the fill original color
picPos0= picPos;
picPos = temp_fill_start;
if((refmode|1) && picDrawEnabled0){
if(picDrawEnabled_orig){
if(col_pic!=col_pic_orig){
if(col_pic==15){
if(draw_pic_orig){
addCode(0xf1);
}
else{
temp_pic->node=0xf1;
picPos=temp_pic->next;
dldelete();
}
}
else{//col_pic != 15
if(draw_pic_orig){
addCode(0xf0);
addCode(col_pic);
}
else{
(temp_pic->next)->node=col_pic;
}
}
}
}
else{//!picDrawEnabled_orig
if(col_pic!=15){
addCode(0xf0);
addCode(col_pic);
}
}
}
if((refmode|2) && priDrawEnabled0){
if(priDrawEnabled_orig){
if(col_pri!=col_pri_orig){
if(col_pri==4){
if(draw_pri_orig){
addCode(0xf3);
}
else{
temp_pri->node=0xf3;
picPos=temp_pri->next;
dldelete();
}
}
else{//col_pri != 4
if(draw_pri_orig){
addCode(0xf2);
addCode(col_pri);
}
else{
(temp_pri->next)->node=col_pri;
}
}
}
}
else{//!priDrawEnabled_orig
if(col_pri!=4){
addCode(0xf2);
addCode(col_pri);
}
}
}
temp = temp_fill_end;
col_pic_new=col_pri_new=-1;
draw_pic_new=draw_pri_new = false;
if(temp){
do{
switch(temp->node){
case 0xf0:
col_pic_new = (temp->next)->node;
break;
case 0xf1:
col_pic_new = -2;
break;
case 0xf2:
col_pri_new = (temp->next)->node;
break;
case 0xf3:
col_pri_new = -2;
break;
default:
if(temp->node >= 0xf4 && temp->node < 0xff){
if(col_pic_new==-1)draw_pic_new=true;
if(col_pri_new==-1)draw_pri_new=true;
}
break;
}
temp = temp->next;
}while(temp && (col_pic_orig==-1 || col_pri_orig==-1));
picPos = temp_fill_end;
if((refmode|1) && picDrawEnabled0){
if(col_pic != col_pic_orig){
if(draw_pic_new){
if(picDrawEnabled_orig){
addCode(0xf0);
addCode(col_pic_orig);
}
else{
if(col_pic!=0x15){
addCode(0xf1);
}
}
}
}
}
if((refmode|2) && priDrawEnabled0){
if(col_pri != col_pri_orig){
if(draw_pri_new){
if(priDrawEnabled_orig){
addCode(0xf2);
addCode(col_pri_orig);
}
else{
if(col_pri!=0x4){
addCode(0xf3);
}
}
}
}
}
}
picPos = picPos0;
draw();
}
//*************************************************
void Picture::draw()
{
byte action;
struct picCodeNode *temp,*temp_fill_start,*temp_fill_end;
int refmode;
bool finishedPic=false;
int pC, pN;
memset(picture, 15, MAX_W*MAX_H); /* Visual screen default, white */
memset(priority, 4, MAX_W*MAX_H); /* Priority screen default, red */
rpos = QUMAX;
spos = 0;
picDrawEnabled = false;
priDrawEnabled = false;
picColour = priColour = 0;
tool=-1;
if ((picStart != NULL) && (picLast != NULL)) {
pC = patCode;
pN = patNum;
patCode = patNum = 0;
temp = picStart;
if (picPos != picStart) do {
action = getCode(&temp);
switch (action) {
case 0xF0: picColour = getCode(&temp);
picDrawEnabled = true;
break;
case 0xF1: picDrawEnabled = false; break;
case 0xF2: priColour = getCode(&temp);
priDrawEnabled = true;
break;
case 0xF3: priDrawEnabled = false; break;
case 0xF4: tool=T_STEP; yCorner(&temp); break;
case 0xF5: tool=T_STEP; xCorner(&temp); break;
case 0xF6: tool=T_LINE; absoluteLine(&temp); break;
case 0xF7: tool=T_PEN; relativeDraw(&temp); break;
case 0xF8: tool=T_FILL;
temp_fill_start = temp->prior;
fill(&temp);
temp_fill_end = temp;
if(refill_pic || refill_pri){
//find which FILL filled the selected area
refmode=0;
if(refill_pic && picGetPixel(refill_x,refill_y)!=15){
refmode |= 1;
refill_pic=false;
}
if(refill_pri && priGetPixel(refill_x,refill_y)!=4){
refill_pri=false;
refmode |= 2;
}
if(refmode){
refill(temp_fill_start,temp_fill_end,refmode);
}
if(!refill_pic && !refill_pri)return;
}
break;
case 0xF9: patCode = getCode(&temp); break;
case 0xFA: tool=T_BRUSH; plotBrush(&temp); break;
case 0xFF: finishedPic=true; break;
default:
printf("Unknown picture code : %X\n", action);
printf("%p %p %p\n", picLast, picPos, temp);
break;
}
} while((temp != picPos) && !finishedPic);
patCode = pC;
patNum = pN;
}
}
//**************************************************
void Picture::init()
{
clear_tools();
priDrawEnabled = picDrawEnabled = false;
addMode = INS_MODE;
curp = &points0;
points0.n=points1.n=0;
newp=&points;
add_pic=add_pri=false;
}
//**************************************************
void Picture::newpic()
{
freeList();
draw();
init();
}
//**************************************************
void Picture::addCode(byte code) /* To the list */
{
struct picCodeNode *temp;
temp = (struct picCodeNode *)malloc(sizeof(picCodes));
if (temp == 0) {
printf("Memory allocation problem.");
exit(0);
}
temp->node = code;
dlstore(temp);
}
//**************************************************
void Picture::replaceCode(byte code)
{
struct picCodeNode *temp;
temp = (struct picCodeNode *)malloc(sizeof(picCodes));
if (temp == 0) {
printf("Memory allocation problem.");
exit(0);
}
temp->node = code;
addMode = OVR_MODE;
dlstore(temp);
addMode = INS_MODE;
}
//**************************************************
void Picture::addPatCode()
{
int pat=0;
pat = (brushSize & 0x07);
if (brushShape == SQUARE) pat |= 0x10;
if (brushTexture == SPRAY) pat |= 0x20;
addCode(pat);
}
//**************************************************
void Picture::adjustDisp(int *dX, int *dY)
//for pen
{
if (*dX > 6) *dX = 6;
if (*dX < -6) *dX = -6;
if (*dY > 7) *dY = 7;
if (*dY < -7) *dY = -7;
}
//**************************************************
void Picture::clear_tools()
{
numClicks=0;
stepClicks=0;
firstClick=true;
tool=-1;
}
//**************************************************
void Picture::init_tool()
{
numClicks=0;
stepClicks=0;
firstClick=true;
}
//**************************************************
void Picture::home_proc()
{
moveToStart();
draw();
init_tool();
}
//**************************************************
void Picture::left_proc()
{
moveBackAction();
draw();
init_tool();
}
//**************************************************
void Picture::right_proc()
{
moveForwardAction();
draw();
init_tool();
}
//**************************************************
void Picture::end_proc()
{
moveToEnd();
draw();
init_tool();
}
//**************************************************
void Picture::del_proc()
{
removeAction();
draw();
init_tool();
}
//**************************************************
void Picture::wipe_proc()
{
wipeAction();
draw();
init_tool();
}
//**************************************************
void Picture::tool_proc(int k)
{
tool=k;
numClicks=0;
stepClicks=0;
firstClick=true;
}
//**************************************************
void Picture::set_brush(int mode,int val)
{
switch(mode){
case 0:
brushSize = val;
break;
case 1:
brushShape = val;
break;
case 2:
brushTexture = val;
break;
}
numClicks=0;
}
//**************************************************
void Picture::choose_color(int button,int color)
{
if(button == M_LEFT){
add_pic=true;
if(color==-1){ //off
// addCode(0xF1);
code_pic=0xF1;
picDrawEnabled = false;
}
else{
picColour = color;
// addCode(0xF0);
//addCode(picColour);
code_pic=0xF0;
col_pic=picColour;
picDrawEnabled = true;
}
}
else{
add_pri=true;
if(color==-1){ //off
// addCode(0xF3);
code_pri=0xF3;
priDrawEnabled = false;
}
else{
priColour = color;
//addCode(0xF2);
//addCode(priColour);
code_pri=0xF2;
col_pri=priColour;
priDrawEnabled = true;
}
}
init_tool();
curcol=(drawing_mode)?priColour:picColour;
}
//**************************************************
int Picture::move_action(int newX, int newY)
{
int ret=0; //1 - draw "temporary" line on screen
switch(tool){
case T_LINE:
if(numClicks==0)break;
normline2(clickX>>1, clickY, newX>>1, newY);
ret=1;
break;
case T_PEN:
if(numClicks==0)break;
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
adjustDisp(&dX, &dY);
normline2(clickX>>1, clickY,(clickX>>1)+dX, clickY+dY);
ret=1;
break;
case T_STEP:
if(stepClicks==0)break;
switch (stepClicks) {
case 1:
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
if (abs(dX) > abs(dY)) dY = 0;
else dX = 0;
normline2(clickX>>1, clickY, (clickX>>1)+dX, clickY+dY);
ret=1;
break;
default:
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
if (stepClicks % 2) dX = 0;
else dY = 0;
normline2(clickX>>1, clickY, (clickX>>1)+dX, clickY+dY);
ret=1;
break;
}
break;
}
return ret;
}
//**************************************************
int Picture::button_action(int newX, int newY)
{
int disp;
int ret=0;
if(!(tool == T_FILL && !okToFill(newX>>1, newY))){
if(add_pic){
addCode(code_pic);
if(code_pic==0xf0){
addCode(col_pic);
}
add_pic=false;
}
if(add_pri){
addCode(code_pri);
if(code_pri==0xf2){
addCode(col_pri);
}
add_pri=false;
}
}
switch(tool){
case T_LINE:
switch (numClicks++) {
case 0: break;
case 1:
addCode(0xF6);
addCode(clickX>>1);
addCode(clickY);
ret=1;
[[fallthrough]];
default:
addCode(newX>>1);
addCode(newY);
ret=1;
drawline(clickX>>1, clickY, newX>>1, newY);
break;
}
clickX = newX;
clickY = newY;
curp = &points0;
points0.n=points1.n=0;
break;
case T_PEN:
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
adjustDisp(&dX, &dY);
switch (numClicks++) {
case 0:
clickX = newX;
clickY = newY;
break;
case 1:
addCode(0xF7);
addCode(clickX>>1);
addCode(clickY);
[[fallthrough]];
default:
if (dX < 0) disp = (0x80 | ((((-1)*dX)-0) << 4));
else disp = (dX << 4);
if (dY < 0) disp |= (0x08 | (((-1)*dY)-0));
else disp |= dY;
addCode(disp);
ret=1;
drawline(clickX>>1, clickY,
(clickX>>1)+dX, (clickY)+dY);
clickX = clickX + (dX<<1);
clickY = clickY + dY;
break;
}
curp = &points0;
points0.n=points1.n=0;
break;
case T_STEP:
switch (stepClicks) {
case 0:
clickX = newX;
clickY = newY;
break;
case 1:
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
if (abs(dX) > abs(dY)) { /* X or Y corner */
dY = 0;
stepClicks++;
addCode(0xF5);
addCode(clickX>>1);
addCode(clickY);
addCode((clickX>>1)+dX);
}
else {
dX = 0;
addCode(0xF4);
addCode(clickX>>1);
addCode(clickY);
addCode((clickY)+dY);
}
ret=1;
drawline(clickX>>1, clickY,
(clickX>>1)+dX, (clickY)+dY);
clickX = clickX + (dX<<1);
clickY = clickY + dY;
break;
default:
dX = ((newX>>1) - (clickX>>1));
dY = ((newY) - (clickY));
if (stepClicks % 2) {
dX = 0;
addCode((clickY)+dY);
}
else {
dY = 0;
addCode((clickX>>1)+dX);
}
ret=1;
drawline(clickX>>1, clickY,
(clickX>>1)+dX, (clickY)+dY);
clickX = clickX + (dX<<1);
clickY = clickY + dY;
break;
}
stepClicks++;
curp = &points0;
points0.n=points1.n=0;
break;
case T_FILL:
if(!(okToFill(newX>>1, newY))){
status(0);
refill_x = newX>>1;
refill_y = newY;
refill_pic = refill_pri = true;
draw();
ret=1;
init();
status(1);
tool = T_FILL;
init_tool();
break;
}
agiFill(newX>>1, newY);
if (firstClick) {
addCode(0xF8);
firstClick = false;
}
addCode(newX>>1);
addCode(newY);
ret=1;
break;
case T_BRUSH:
if (numClicks == 0) {
addCode(0xF9);
addPatCode();
addCode(0xFA);
}
numClicks++;
patCode = brushSize;
if (brushShape == SQUARE) patCode |= 0x10;
if (brushTexture == SPRAY) patCode |= 0x20;
patNum = ((rand() % 0xEE) >> 1) & 0x7F;
plotPattern(newX>>1, newY);
if (brushTexture == SPRAY) addCode(patNum<<1);
addCode(newX>>1);
addCode(newY);
ret=1;
}
return ret;
}
//***************************************************
void Picture::set_mode(int mode)
{
drawing_mode=mode;
if(mode==0){
pptr = picture;
curcol = picColour;
}
else{
pptr = priority;
curcol = priColour;
}
}
//***************************************************
void Picture::putpix2(int x,int y)
{
byte c;
QColor cc;
if(x<0||y<0||x>=MAX_W||y>=MAX_HH){
return;
}
//save the pixels under the line to be drawn
//so it can be restored when the line is moved
if(bg_on){ //if background is on - must save the contents of the background image
x<<=1;
curp->p[curp->n].x=x;
curp->p[curp->n].y=y;
c=pptr[y*MAX_W+x];
if((c==15&&pptr==picture)||(c==4&&pptr==priority)){
curp->p[curp->n].cc=QColor(bgpix->pixel(x,y));
curp->n++;
curp->p[curp->n].x=x+1;
curp->p[curp->n].y=y;
curp->p[curp->n].cc=QColor(bgpix->pixel(x+1,y));
curp->n++;
}
else{
cc=egacolor[c];
curp->p[curp->n].cc=cc;
curp->n++;
curp->p[curp->n].x=x+1;
curp->p[curp->n].y=y;
curp->p[curp->n].cc=cc;
curp->n++;
}
x>>=1;
}
else{ //save the pixels
curp->p[curp->n].x=x;
curp->p[curp->n].y=y;
curp->p[curp->n].c=pptr[y*MAX_W+(x<<1)];
curp->n++;
}
newp->p[newp->n].x=x;
newp->p[newp->n].y=y;
newp->p[newp->n].c=curcol;
newp->n++;
}
//***************************************************
void Picture::normline2(int x1, int y1, int x2, int y2)
{
int height, width;
float x, y, addX, addY;
height = (y2 - y1);
width = (x2 - x1);
addX = (height==0?height:(float)width/abs(height));
addY = (width==0?width:(float)height/abs(width));
points.n=0;
curp->n=0;
if (abs(width) > abs(height)) {
y = y1;
addX = (width == 0? 0 : (width/abs(width)));
for (x=x1; x!=x2; x+=addX) {
putpix2(round(x, addX), round(y, addY));
y+=addY;
}
putpix2(x2,y2);
}
else {
x = x1;
addY = (height == 0? 0 : (height/abs(height)));
for (y=y1; y!=y2; y+=addY) {
putpix2(round(x, addX), round(y, addY));
x+=addX;
}
putpix2(x2,y2);
}
if(curp==&points0)curp= (&points1);
else curp= (&points0);
}
//***************************************************
void Picture::viewData(TStringList *data)
{
struct picCodeNode *temp;
byte c;
char tmp1[6];
data->lfree();
if (picStart == NULL) { /* Black picture */
data->add("ff");
return ;
}
temp = picStart;
tmp[0]=0;
for(temp=picStart;temp!=NULL;temp=temp->next){
c = temp->node;
if(c>=0xf0 && tmp[0]){
data->add(tmp);
sprintf(tmp,"%02x ",c);
}
else{
sprintf(tmp1,"%02x ",c);
strcat(tmp,tmp1);
}
}
if(tmp1[0]!=0){
strcat(tmp,tmp1);
data->add(tmp);
}
data->add("ff");
}
//***************************************************
void Picture::status(int mode)
{
if(mode==0){
picDrawEnabled0 = picDrawEnabled;
priDrawEnabled0 = priDrawEnabled;
picColour0=picColour;
priColour0=priColour;
}
else{
picDrawEnabled = picDrawEnabled0;
priDrawEnabled = priDrawEnabled0;
picColour=picColour0;
priColour=priColour0;
}
}