qt-agistudio/src/bpicture.cpp

546 lines
14 KiB
C++

/*
* QT AGI Studio :: Copyright (C) 2000 Helen Zommer
*
* Almost all of the picture processing code is taken from showpic.c
* by Lance Ewing <lance.e@ihug.co.nz>
*
* 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 "game.h"
#include "menu.h"
#include "picture.h"
#include <stdio.h>
#include <sys/types.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
BPicture *ppicture;
//********************************************
//"bytemap" picture for preview - it is not going to be edited,
//so there is no need for the linked list and other things from the Picture class
BPicture::BPicture()
{
picture = (byte **)malloc(MAX_H*sizeof(byte *));
priority = (byte **)malloc(MAX_H*sizeof(byte *));
for(int i=0;i<MAX_H;i++){
picture[i] = (byte *)malloc(MAX_W);
priority[i] = (byte *)malloc(MAX_W);
}
}
//****************************************************
void BPicture::qstore(byte q)
{
if (spos+1==rpos || (spos+1==QUMAX && !rpos)) {
//nosound();
return;
}
buf[spos] = q;
spos++;
if (spos==QUMAX) spos = 0; /* loop back */
}
//********************************************
byte BPicture::qretrieve()
{
if (rpos==QUMAX) rpos=0; /* loop back */
if (rpos==spos) {
return EMPTY;
}
rpos++;
return buf[rpos-1];
}
/**************************************************************************
** picPSet
**
** Draws a pixel in the picture screen.
**************************************************************************/
void BPicture::picPSet(word x, word y)
{
word vx, vy;
vx = (x << 1);
vy = y;
if (vx > 319) return;
if (vy > 199) return;
picture[vy][vx] = picColour;
picture[vy][vx+1] = picColour;
}
/**************************************************************************
** priPSet
**
** Draws a pixel in the priority screen.
**************************************************************************/
void BPicture::priPSet(word x, word y)
{
word vx, vy;
vx = (x << 1);
vy = y;
if (vx > 319) return;
if (vy > 199) return;
priority[vy][vx] = priColour;
priority[vy][vx+1] = priColour;
}
/**************************************************************************
** pset
**
** Draws a pixel in each screen depending on whether drawing in that
** screen is enabled or not.
**************************************************************************/
void BPicture::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 BPicture::picGetPixel(word x, word y)
{
word vx, vy;
vx = (x << 1);
vy = y;
if (vx > 319) return(4);
if (vy > 199) return(4);
return (picture[vy][vx]);
}
/**************************************************************************
** priGetPixel
**
** Get colour at x,y on the priority page.
**************************************************************************/
byte BPicture::priGetPixel(word x, word y)
{
word vx, vy;
vx = (x << 1);
vy = y;
if (vx > 319) return(4);
if (vy > 199) return(4);
return (priority[vy][vx]);
}
/**************************************************************************
** 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 BPicture::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 BPicture::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 BPicture::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 BPicture::agiFill(word x, word y)
{
byte x1, y1;
rpos = spos = 0;
qstore(x);
qstore(y);
// printf("fill %d %d\n",x,y);
for (;;) {
x1 = qretrieve();
y1 = qretrieve();
// printf("x1=%d y1=%d\n");
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 BPicture::xCorner(byte **data)
{
byte x1, x2, y1, y2;
x1 = *((*data)++);
y1 = *((*data)++);
pset(x1,y1);
for (;;) {
x2 = *((*data)++);
if (x2 >= 0xF0) break;
drawline(x1, y1, x2, y1);
x1 = x2;
y2 = *((*data)++);
if (y2 >= 0xF0) break;
drawline(x1, y1, x1, y2);
y1 = y2;
}
(*data)--;
}
/**************************************************************************
** yCorner
**
** Draws an yCorner (drawing action 0xF4)
**************************************************************************/
void BPicture::yCorner(byte **data)
{
byte x1, x2, y1, y2;
x1 = *((*data)++);
y1 = *((*data)++);
pset(x1, y1);
for (;;) {
y2 = *((*data)++);
if (y2 >= 0xF0) break;
drawline(x1, y1, x1, y2);
y1 = y2;
x2 = *((*data)++);
if (x2 >= 0xF0) break;
drawline(x1, y1, x2, y1);
x1 = x2;
}
(*data)--;
}
/**************************************************************************
** relativeDraw
**
** Draws short lines relative to last position. (drawing action 0xF7)
**************************************************************************/
void BPicture::relativeDraw(byte **data)
{
byte x1, y1, disp;
char dx, dy;
x1 = *((*data)++);
y1 = *((*data)++);
pset(x1, y1);
for (;;) {
disp = *((*data)++);
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;
}
(*data)--;
}
/**************************************************************************
** fill
**
** Agi flood fill. (drawing action 0xF8)
**************************************************************************/
void BPicture::fill(byte **data)
{
byte x1, y1;
for (;;) {
if ((x1 = *((*data)++)) >= 0xF0) break;
if ((y1 = *((*data)++)) >= 0xF0) break;
agiFill(x1, y1);
}
(*data)--;
}
/**************************************************************************
** absoluteLine
**
** Draws long lines to actual locations (cf. relative) (drawing action 0xF6)
**************************************************************************/
void BPicture::absoluteLine(byte **data)
{
byte x1, y1, x2, y2;
x1 = *((*data)++);
y1 = *((*data)++);
pset(x1, y1);
for (;;) {
if ((x2 = *((*data)++)) >= 0xF0) break;
if ((y2 = *((*data)++)) >= 0xF0) break;
drawline(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
}
(*data)--;
}
#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 BPicture::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 BPicture::plotBrush(byte **data)
{
byte x1, y1;
for (;;) {
if (patCode & 0x20) {
if ((patNum = *((*data)++)) >= 0xF0) break;
patNum = (patNum >> 1 & 0x7f);
}
if ((x1 = *((*data)++)) >= 0xF0) break;
if ((y1 = *((*data)++)) >= 0xF0) break;
plotPattern(x1, y1);
}
(*data)--;
}
//****************************************************
void BPicture::show(byte *picdata,int picsize)
{
byte *data = picdata;
bool stillDrawing = true;
byte action;
for(int i=0;i<MAX_H;i++){
memset(picture[i],15,MAX_W);
memset(priority[i],4,MAX_W);
}
rpos = QUMAX;
spos = 0;
picDrawEnabled = false;
priDrawEnabled = false;
picColour = priColour = 0;
do {
action = *(data++);
switch (action) {
case 0xFF: stillDrawing = 0; break;
case 0xF0: picColour = *(data++);
picDrawEnabled = true;
break;
case 0xF1: picDrawEnabled = false; break;
case 0xF2: priColour = *(data++);
priDrawEnabled = true;
break;
case 0xF3: priDrawEnabled = false; break;
case 0xF4: yCorner(&data); break;
case 0xF5: xCorner(&data); break;
case 0xF6: absoluteLine(&data); break;
case 0xF7: relativeDraw(&data); break;
case 0xF8: fill(&data); break;
case 0xF9: patCode = *(data++); break;
case 0xFA: plotBrush(&data); break;
default: printf("Unknown picture code : %X\n", action); break;
}
}while((data < (data + picsize)) && stillDrawing);
}
//****************************************************