This is BattleView.m in view mode; [Download] [Up]
/*
Copyright 1992, Stefanos Kiakas. All rights reserved.
You may not delete this notice.
*/
#define ABS(x) ( x < 0 ? -x : x) /* macro to calculate absolute value */
#define RANDTARGET ( random() % TARGETS)/* select a random target to attack */
#define RANDLAUNCH ( ( random() % 440 ) + 20 ) /* x pos. of attack missles */
#define PLACECITY(x) ( random() % x ) /* selct position to place city */
#define NULL 0 /* define end of list marker */
#define MAXRADIUS 15.0 /* maximum radius of explosions */
#define TERRAINCOLOUR 0.20
#define TIMEINTERVAL 0.1
#define DELTA_TIME 100
#define RADINC 1.0 /* radius increment of explosion circles */
#define MAXDEFEXP 15
#define MAXDEFLAUNCH 10 /* maximum number of defensive missiles */
#define MAXATCKMSSLS 10 /* maximum number of attack missiles */
/* define launch coordinates for defensive missiles */
#define LAUNCH_X 250.0
#define LAUNCH_Y 25.0
#define DISP 3.0
#define CDX 18.0 // center of target from display coordinates
#define CITYBONUS 500 // city saved bonus
#define MSLBONUS 50 // missiles saved bonus
#define MSLDSTRCT 100 // attack missle intercepted ( destroyed ) bonus
#define BONUSMARK 7000 // give bonus city every so many points
#define OLAUNCH_Y 390.0; /* Y launch position of attack missles */
#define DX 5.0
#define DY 5.0
#define H_CURSOR 330
#define W_CURSOR 460
#define Y_CURSOR 50
#define X_CURSOR 20
#define H_TRG Y_CURSOR + H_CURSOR
#define W_TRG X_CURSOR + W_CURSOR
#define Y_TRG Y_CURSOR
#define X_TRG X_CURSOR
#define MAXNDIFF 2.0 /* maximum difficulty for novice level */
#define MAXEDIFF 2.5 /* maximum difficulty for experienced level */
#define MAXPDIFF 3.0 /* maximum difficulty for pro level */
#define STARTNDIFF 0.8 /* starting difficulty for novice level */
#define STARTEDIFF 0.75 /* starting difficulty for experienced level */
#define STARTPDIFF 0.9 /* starting difficulty for por level */
#define DIFFINCN 0.1 /* increase in difficulty after each attack for novice level */
#define DIFFINCE 0.25 /* increase in difficulty after each attack for experienced level */
#define DIFFINCP 0.3 /* increase in difficulty after each attack for pro level */
#define DISPLAYDELAY 50000
#define SCOREXPOS 150
#define SCOREYPOS 250
#import "markLoc.h"
#import "plotTrajectory.h"
#import "scorePhaseCities.h"
#import "scorePhaseMissiles.h"
#include <sys/param.h>
#import <libc.h>
#import "BattleView.h"
#import <appkit/appkit.h>
#import <soundkit/Sound.h>
@implementation BattleView
- initFrame:(const NXRect *) frm
{
char appD[MAXPATHLEN];
NXPoint mySpot;
[super initFrame:frm ];
[self allocateGState];
gameRunning = FALSE;
gamePaused = FALSE;
sprintf(appD,"%s/%s",[self appDirectory],"RedAlert.snd");
redAlertSound = [ [Sound alloc] initFromSoundfile:appD ];
cityBitmap = [NXImage findImageNamed:"city3.tiff"];
launcherBitmap = [NXImage findImageNamed:"mBase.tiff"];
cursorImage=[NXCursor newFromImage:[NXImage newFromSection:"targetD3.tiff"]];
cBounds.origin.x = X_CURSOR;
cBounds.origin.y = Y_CURSOR;
cBounds.size.width= W_CURSOR;
cBounds.size.height = H_CURSOR;
mySpot.x = 8;
mySpot.y = 7;
[cursorImage setHotSpot:&mySpot];
ndiffstart = STARTNDIFF;
ndiffinc = DIFFINCN;
ndiffmax = MAXNDIFF;
return self;
}
/*
This function was shamelessly copied from the examples provided.
*/
- (const char *)appDirectory
{
char *suffix;
strcpy(appDirectory, NXArgv[0]);
if (suffix = rindex(appDirectory,'/'))
*suffix = '\0';
if (appDirectory) chdir(appDirectory);
getwd(appDirectory);
return appDirectory;
}
- selectExperienced:sender
{
ndiffinc = DIFFINCE;
ndiffstart = STARTEDIFF;
ndiffmax = MAXEDIFF;
return self;
}
- selectNovice:sender
{
ndiffinc = DIFFINCN;
ndiffstart = STARTNDIFF;
ndiffmax = MAXNDIFF;
return self;
}
- selectPro:sender
{
ndiffinc = DIFFINCP;
ndiffstart = STARTPDIFF;
ndiffmax = MAXPDIFF;
return self;
}
- pauseGame:sender
{
void stepFun();
if( gameRunning )
{
gameRunning = FALSE;
gamePaused = TRUE;
DPSRemoveTimedEntry(gameTimer);
}
else if( !gameRunning && gamePaused )
{
gameRunning = TRUE;
gamePaused = FALSE;
gameTimer =DPSAddTimedEntry(TIMEINTERVAL,&stepFun,self,NX_BASETHRESHOLD);
}
return self;
}
- resetCursorRects
{
[self addCursorRect:&cBounds cursor:cursorImage];
return self;
}
- newGame:sender
{
int i;
void stepFun();
unsigned int currentTimeInMs();
MSLE_NODE_PNTR cpdm,fpdm;
MA_NODE_PNTR cpam,fpam;
EXP_PNTR cpe,fpe;
if( !gameRunning && !gamePaused )
{
/* set up current game difficulty levels */
cdiffstart = ndiffstart;
cdiffinc = ndiffinc;
cdiffmax = ndiffmax;
dFactor = cdiffstart;
phaseOne = TRUE;
defensiveMissiles.launched = 0;
defensiveMissiles.next = NULL;
offensiveMissiles.launched = 0;
offensiveMissiles.next = NULL;
defensiveExplosions.exploding = 0;
defensiveExplosions.next = NULL;
for( i = 0; i < CITIES; i++)
if( ( i % 3 ) && ( i %4 ) )
cityStatus[i] = TRUE;
else
cityStatus[i] = FALSE;
// reset number of missiles
aTimeBefore = currentTimeInMs();
// select attack positions
[self selectTargets];
missiles = 40;
score = 0;
lastBonusAt = 0;
displayBase = TRUE;
[outMissiles setIntValue:missiles];
[outScore setIntValue:score];
[window makeKeyAndOrderFront:self];
gameRunning = TRUE;
gameTimer =DPSAddTimedEntry(TIMEINTERVAL,&stepFun,self,NX_BASETHRESHOLD);
[self display];
}
else if( gameRunning && !gamePaused )
{
gameRunning = FALSE;
DPSRemoveTimedEntry(gameTimer);
// remove entries in linked lists
cpdm = defensiveMissiles.next;
while( cpdm != NULL )
{
fpdm = cpdm;
cpdm = cpdm->next;
free(fpdm);
}
/* remove attack missiles from list, and free memory */
cpam = offensiveMissiles.next;
while( cpdm != NULL )
{
fpam = cpam;
cpam = cpam->next;
free(fpam);
}
/* remove air explosions from list and free memory */
cpe = defensiveExplosions.next;
while( cpe != NULL )
{
fpe = cpe;
cpe = cpe->next;
free(fpe);
}
/* remove surface explosions from list and free memory */
cpe = surfaceExplosions.next;
while( cpe != NULL )
{
fpe = cpe;
cpe = cpe->next;
free(fpe);
}
}
return self;
}
unsigned currentTimeInMs()
{
struct timeval curTime;
gettimeofday(&curTime, NULL);
return ((unsigned)curTime.tv_sec)*1000+curTime.tv_usec / 1000;
}
- step
{
NXEvent dummyEvent;
unsigned int timeNow,dTime;
do
{
timeNow = currentTimeInMs();
dTime = timeNow - aTimeBefore;
if( dTime > DELTA_TIME )
{
aTimeBefore = timeNow;
if( defensiveMissiles.launched != 0 )
[self missilePath];
if( defensiveExplosions.exploding > 0 )
[self expDraw];
if( offensiveMissiles.launched > 0)
[self attackMissilesPath];
if( surfaceExplosions.exploding > 0 )
[self surfaceExplosionsDraw];
}
if(
defensiveMissiles.launched == 0 &&
defensiveExplosions.exploding == 0 &&
offensiveMissiles.launched == 0 &&
surfaceExplosions.exploding == 0
)
{
if( !phaseOne )
[ self bonusPoints];
[self nextLevel];
}
NXPing();
}
while ([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent] == NULL );
return self;
}
- drawSelf:(const NXRect *)r :(int)c
{
int i;
PSsetgray(1.0);
NXRectFill(r);
PSsetgray(NX_BLACK);
NXFrameRect(r);
// draw terrain
PSsetgray(TERRAINCOLOUR);
PSnewpath();
PSmoveto(0.0,0.0);
PSrlineto(0.0,5.0);
PSrlineto(50.0,0.0);
PSrlineto(5.0,5.0);
PSrlineto(40.0,0.0);
PSrlineto(5.0,-5.0);
PSrlineto(80.0,0.0);
PSrlineto(5.0,5.0);
PSrlineto(40.0,0.0);
PSrlineto(5.0,5.0);
PSrlineto(40.0,0.0);
PSrlineto(5.0,-5.0);
PSrlineto(40.0,0.0);
PSrlineto(5.0,-5.0);
PSrlineto(80.0,0.0);
PSrlineto(5.0,5.0);
PSrlineto(40.0,0.0);
PSrlineto(5.0,-5.0);
PSrlineto(50.0,0.0);
PSrlineto(0.0,-5.0);
PSclosepath();
PSfill();
PSflushgraphics();
if( displayBase ) /* display missles base if it has not been destroyed */
[self drawLaunchPad];
for(i=0;i<CITIES; i++)
if( cityStatus[i] )
[self drawCityBM:CBarray[i].coord];
return self;
}
- drawCityBM:(NXPoint ) dcPt
{
[cityBitmap composite:NX_SOVER toPoint:&dcPt];
return self;
}
- drawLaunchPad
{
NXPoint Pt;
Pt.x = 230.0;
Pt.y = 15.0;
[launcherBitmap composite:NX_SOVER toPoint:&Pt];
return self;
}
- drawMissile
{
id missileBitmap = [NXImage findImageNamed:"missileIcon.tiff"];
NXPoint Pt;
Pt.x = 160.0;
Pt.y = 190.0;
[missileBitmap composite:NX_COPY toPoint:&Pt];
return self;
}
- mouseDown:(NXEvent *)theEvent
{
NXPoint sPt;
int pntInRect();
MSLE_NODE_PNTR mLaunch,p;
sPt = theEvent->location;
[self convertPoint:&sPt fromView:nil];
if ( defensiveMissiles.launched < MAXDEFLAUNCH && missiles > 0 && pntInRect(&sPt))
{
[outMissiles setIntValue:--missiles];
defensiveMissiles.launched++;
[self lockFocus];
markLoc(sPt.x,sPt.y);
[self unlockFocus];
mLaunch = malloc(sizeof(MSLE_NODE));
mLaunch->dPos = sPt;
mLaunch->cPos.x = LAUNCH_X;
mLaunch->cPos.y = LAUNCH_Y;
mLaunch->next = NULL;
// must check for zeros
if (ABS(sPt.x - LAUNCH_X) > ABS(sPt.y - LAUNCH_Y))
{
mLaunch->delta_x = DISP;
mLaunch->delta_y = DISP *(sPt.y - LAUNCH_Y) /(sPt.x - LAUNCH_X);
}
else
{
mLaunch->delta_y = DISP;
mLaunch->delta_x = DISP *(sPt.x - LAUNCH_X)/ (sPt.y - LAUNCH_Y);
}
if ( defensiveMissiles.next == NULL)
defensiveMissiles.next = mLaunch;
else
{
p = defensiveMissiles.next;
while (p->next != NULL )
p = p->next;
p->next = mLaunch;
}
}
return self;
}
- expDraw
{
EXP_PNTR cp,pp;
pp = cp = defensiveExplosions.next;
[self lockFocus];
while (cp != NULL )
{
if (cp->rad > MAXRADIUS )
{
cp->maxReached = YES;
cp->rad += ( cp->maxReached ) ? -RADINC: RADINC ;
pp = cp;
cp = cp->next;
}
else if ( ( cp->rad <= 0.0 ) && cp->maxReached )
{
if ( defensiveExplosions.next == cp )
{
pp = defensiveExplosions.next = cp->next;
free(cp);
cp = pp;
}
else
{
pp->next = cp->next;
free(cp);
cp = pp->next;
}
defensiveExplosions.exploding--;
}
else
{
PSsetgray((cp->maxReached) ? NX_WHITE : NX_BLACK );
PSarc(cp->cPt.x,cp->cPt.y,cp->rad,0.0,360.0);
PSstroke();
PSflushgraphics();
cp->rad += (( cp->maxReached ) ? -RADINC: RADINC ) ;
pp = cp;
cp = cp->next;
}
}
[self unlockFocus];
return self;
}
- surfaceExplosionsDraw
{
EXP_PNTR cp,pp;
pp = cp = surfaceExplosions.next;
[self lockFocus];
while (cp != NULL )
{
if (cp->rad > MAXRADIUS )
{
cp->maxReached = YES;
cp->rad += ( cp->maxReached ) ? -RADINC: RADINC ;
pp = cp;
cp = cp->next;
}
else if ( ( cp->rad <= 0.0 ) && cp->maxReached )
{
if ( surfaceExplosions.next == cp )
{
pp = surfaceExplosions.next = cp->next;
PSnewpath();
PSsetgray( NX_WHITE);
PSmoveto(cp->cPt.x,cp->cPt.y);
PSrlineto(-10,0);
PSrlineto(0,20);
PSrlineto(30,0);
PSrlineto(0,-20);
PSrlineto(-10,0);
PSfill();
PSflushgraphics();
free(cp);
cp = pp;
}
else
{
pp->next = cp->next;
PSnewpath();
PSsetgray( NX_WHITE);
PSmoveto(cp->cPt.x,cp->cPt.y);
PSrlineto(-10,0);
PSrlineto(0,20);
PSrlineto(30,0);
PSrlineto(0,-20);
PSrlineto(-10,0);
PSfill();
PSflushgraphics();
free(cp);
cp = pp->next;
}
surfaceExplosions.exploding--;
}
else
{
[self lockFocus];
PSsetgray((cp->maxReached) ? NX_WHITE : NX_BLACK );
PSarc(cp->cPt.x,cp->cPt.y,cp->rad,1.0,180.0);
PSstroke();
PSflushgraphics();
[self unlockFocus];
cp->rad += (( cp->maxReached ) ? -RADINC: RADINC ) ;
pp = cp;
cp = cp->next;
}
}
[self unlockFocus];
return self;
}
- missilePath
{
NXPoint nPos; // new missile position
MSLE_NODE_PNTR cp,pp,dp;
EXP_PNTR nExp;
pp = cp = defensiveMissiles.next;
[self lockFocus];
while ( cp != NULL )
{
if ( cp->dPos.y <= cp->cPos.y) /* missile has reached it destination */
{
PSmoveto(LAUNCH_X,LAUNCH_Y);
PSlineto(cp->dPos.x,cp->dPos.y);
PSsetgray(NX_WHITE);
PSsetlinewidth(2.0);
PSstroke();
PSsetgray(NX_BLACK);
PSflushgraphics();
if ( cp == defensiveMissiles.next )
{
defensiveMissiles.next = cp->next;
dp = cp;
cp = defensiveMissiles.next;
}
else
{
pp->next = cp->next;
dp = cp;
cp = pp->next;
}
nExp = malloc(sizeof(EXP_NODE));
nExp->cPt = dp->dPos;
nExp->rad = 0.0;
nExp->maxReached = NO;
nExp->next = NULL;
free(dp);
[self addToAirExplosions:nExp];
defensiveMissiles.launched--;
}
else /* calculate and plot next position */
{
nPos.x = cp->cPos.x + cp->delta_x;
if( cp->delta_x < 0 ) /* make sure that point is not past detonation point */
{
if( nPos.x < cp->dPos.x)
nPos.x = cp->dPos.x;
}
else
{
if( nPos.x > cp->dPos.x )
nPos.x = cp->dPos.x;
}
nPos.y = cp->cPos.y + cp->delta_y;
if ( cp->dPos.y > cp->cPos.y )
{
plotTrajectory(cp->cPos.x,cp->cPos.y,nPos.x,nPos.y);
cp->cPos = nPos;
}
pp = cp;
cp = cp->next;
}
}
[self unlockFocus];
return self;
}
- nextLevel
{
int cities,i;
cities = 0;
for(i=0;i<CITIES; i++)
if( cityStatus[ i ] )
cities++;
if( cities == 0 )
gameRunning = FALSE;
else
{
if( phaseOne)
phaseOne = FALSE;
else
{
phaseOne = TRUE;
if( dFactor < cdiffmax )
dFactor += cdiffinc;
missiles = 40;
displayBase = TRUE;
}
// reset time
aTimeBefore = currentTimeInMs();
[outMissiles setIntValue:missiles];
[outScore setIntValue:score];
[self display];
// calculate attack positions
[self selectTargets];
}
return self;
}
- bonusPoints
{
NXPoint cityPt;
int cities,i,re;
int vacantPos[ CITIES ],j;
unsigned int stime,etime;
cityPt.x = SCOREXPOS;
cityPt.y = SCOREYPOS;
cities = 0;
for(i=0;i<CITIES; i++)
if( cityStatus[ i ] )
cities++;
[self lockFocus];
[self drawCityBM:cityPt];
[self unlockFocus];
for(i=0; i <= cities; i++ )
{
[self lockFocus];
scorePhaseCities(i,i*CITYBONUS,&re);
[self unlockFocus];
}
etime = stime = currentTimeInMs();
while( etime - stime < 100 )
etime = currentTimeInMs();
score += cities * CITYBONUS;
[outScore setIntValue:score];
[self lockFocus];
[self drawMissile];
[self unlockFocus];
for(i=0; i <= missiles; i++ )
{
[self lockFocus];
scorePhaseMissiles(i,i*MSLBONUS,&re);
[self unlockFocus];
etime = stime = currentTimeInMs();
while( etime - stime < 100 )
etime = currentTimeInMs();
}
score += missiles * MSLBONUS;
[outScore setIntValue:score];
etime = stime = currentTimeInMs();
while( etime - stime < 1000 )
etime = currentTimeInMs();
if( score - lastBonusAt > BONUSMARK )
{
lastBonusAt += BONUSMARK;
j = 0;
for(i=0;i<CITIES ; i++)
if( cityStatus[ i ] == FALSE )
vacantPos[j++] = i; ;
if( j )
cityStatus[ vacantPos[PLACECITY(j)]] = TRUE;
else
score += CITYBONUS;
}
return self;
}
- selectTargets
{
float dx,dy;
MA_NODE_PNTR head,cp,pp;
pp = head = cp = malloc(sizeof(MA_NODE));
while (++offensiveMissiles.launched <= MAXATCKMSSLS ) /* attack missles init */
{
cp->sPos.x = RANDLAUNCH;
cp->sPos.y = OLAUNCH_Y;
cp->targetLoc = RANDTARGET;
cp->dPos.x = CBarray[cp->targetLoc].coord.x + CDX;
cp->dPos.y = CBarray[cp->targetLoc].coord.y;
cp->cPos = cp->sPos;
dx = cp->dPos.x - cp->sPos.x ;
dy = cp->dPos.y - OLAUNCH_Y;
if ( ABS(dy ) > ABS(dx))
{
cp->delta_y = -1.0;
cp->delta_x = ( dx ) / ABS(dy );
}
else
{
cp->delta_y = ( dy )/ABS( dx ) ;
cp->delta_x = ABS(dx ) / dx ;
}
cp->next = malloc(sizeof(MA_NODE));
pp = cp;
cp = cp->next;
}
offensiveMissiles.launched--;
offensiveMissiles.next = head;
pp->next = NULL;
free(cp);
[redAlertSound play];
return self;
}
- addToAirExplosions: (EXP_PNTR )aExp
{
EXP_PNTR ep;
if ( defensiveExplosions.next == NULL )
defensiveExplosions.next = aExp;
else
{
ep = defensiveExplosions.next;
while (ep->next != NULL )
ep = ep->next;
ep->next = aExp;
}
defensiveExplosions.exploding++;
return self;
}
- addToSurfaceExplosions:(EXP_PNTR) aExp
{
EXP_PNTR ep;
if ( surfaceExplosions.next == NULL )
surfaceExplosions.next = aExp;
else
{
ep = surfaceExplosions.next;
while (ep->next != NULL )
ep = ep->next;
ep->next = aExp;
}
surfaceExplosions.exploding++;
return self;
}
- attackMissilesPath
{
MA_NODE_PNTR head,cp,pp;
EXP_PNTR nExp;
NXPoint nPt;
NXRect rect = {{0.0, 0.0}, {1.0, 1.0}};
id image;
NXColor colorAtSomeLoc, colorAt();
float alpha,color;
[self lockFocus];
head = cp = pp = offensiveMissiles.next; /* set up pointers to point at head of attack missile list */
while ( cp != NULL )
{
// calculate next point in trajectory
nPt.x = cp->cPos.x + dFactor * cp->delta_x;
nPt.y = cp->cPos.y + dFactor * cp->delta_y;
// set rect to check if missile has been hit
rect.origin = nPt;
image = [[NXBitmapImageRep alloc] initData:NULL fromRect:&rect];
colorAtSomeLoc = colorAt(image, 0, 0);
[image free];
NXConvertColorToGrayAlpha (colorAtSomeLoc, &color, &alpha);
if ( nPt.y < cp->dPos.y) /* attack missile has arrived at target */
{
offensiveMissiles.launched--;
if ( cp->targetLoc == ( TARGETS - 1 ) )
{
missiles = 0;
[outMissiles setIntValue:missiles];
displayBase = FALSE;
}
else
cityStatus[ cp->targetLoc ] = FALSE;
/* erase missile trajectory */
PSmoveto(cp->cPos.x,cp->cPos.y);
PSlineto(cp->sPos.x,cp->sPos.y);
PSsetgray(NX_WHITE);
PSsetlinewidth(2.0);
PSstroke();
PSsetlinewidth(1.0);
PSflushgraphics();
/* creat an explosion node for surface explosions */
nExp = malloc(sizeof(EXP_NODE));
nExp->cPt = cp->cPos;
nExp->rad = 0.0;
nExp->maxReached = NO;
nExp->next = NULL;
[self addToSurfaceExplosions:nExp]; /* add explosion to surface exp. list */
if ( cp == head )
{
offensiveMissiles.next = head = cp->next;
free(cp);
cp = head;
}
else
{
pp-> next = cp->next;
free(cp);
cp = pp->next;
}
}
else if ( ( ABS(color - NX_BLACK) < 0.005 ) && ( cp->cPos.y > 35 ))
{
offensiveMissiles.launched--; // missile has been destroyed
score += MSLDSTRCT;
[outScore setIntValue:score];
// remove missile trajectory
PSmoveto(cp->cPos.x,cp->cPos.y);
PSlineto(cp->sPos.x,cp->sPos.y);
PSsetgray(NX_WHITE);
PSsetlinewidth(2.0);
PSstroke();
PSsetlinewidth(1.0);
PSflushgraphics();
nExp = malloc(sizeof(EXP_NODE));
nExp->cPt = cp->cPos;
nExp->rad = 0.0;
nExp->maxReached = NO;
nExp->next = NULL;
[self addToAirExplosions:nExp];
// remove missile from list
if ( cp == head )
{
offensiveMissiles.next = head = cp->next;
free(cp);
cp = pp = head;
}
else
{
pp-> next = cp->next;
free(cp);
cp = pp->next;
}
}
else
{
plotTrajectory(cp->cPos.x,cp->cPos.y,nPt.x,nPt.y);
cp->cPos = nPt;
pp = cp;
cp = cp->next;
}
}
[self unlockFocus];
return self;
}
int pntInRect(pnt)
NXPoint *pnt;
{
if( pnt->x >= X_TRG && pnt->y >= Y_TRG && pnt->x <= W_TRG && pnt->y <= H_TRG )
return(TRUE);
else
return(FALSE);
}
void stepFun (DPSTimedEntry timedE, double timeN, void *data)
{
[(id)data step ];
}
/**************************************************************************************************/
/* all the following code has been copied from the NeXT answers code provided by NeXT */
/***************************************************************************************************/
/* Returns the color at a certain pixel location in a bitmap. */
/* This version assumes the bitmap is 1, 2, 4, or 8 bps deep */
/* and its either grayscale or RGB. */
NXColor colorAt(NXBitmapImageRep *image, int x, int y)
{
int sampleCnt, bps, spp, amask;
unsigned char *planes[5], data[5];
NXColorSpace colorSpace;
spp = [image samplesPerPixel];
bps = [image bitsPerSample];
colorSpace = [image colorSpace];
#ifdef CHECK_VALIDITY
if ((bps != 1 && bps != 2 && bps != 4 && bps != 8) ||
(colorSpace != NX_RGBColorSpace ||
colorSpace != NX_OneIsBlackColorSpace ||
colorSpace != NX_OneIsWhiteColorSpace))
{
NXLogError ("colorAt() can't deal with provided image.\n");
return NX_COLORCLEAR;
}
#endif
#ifdef CHECK_RANGE
if ((x < 0) || (y < 0) || (x >= [image pixelsWide]) ||
(y >= [image pixelsHigh]))
{
NXLogError ("Pixel out of bounds in colorAt().\n");
return NX_COLORCLEAR;
}
#endif
[image getDataPlanes:planes];
amask = (1 << bps) - 1; /* 1, 3, 15, 255 for bps = 1, 2, 4, 8 */
/* Get the samples into the data[] array. */
if ([image isPlanar])
{
int pixel = x * bps;
int byteLoc = [image bytesPerRow] * y + (pixel >> 3);
int bitLoc = pixel & 7;
for (sampleCnt = 0; sampleCnt < spp; sampleCnt++)
{
data[sampleCnt] =
((*(planes[sampleCnt]+byteLoc)) >> (8 - bitLoc - bps)) & amask;
}
}
else
{
unsigned char *byteLoc = planes[0] + [image bytesPerRow] * y;
int bitLoc = x * bps * spp;
for (sampleCnt = 0; sampleCnt < spp; sampleCnt++)
{
data[sampleCnt] =
((byteLoc[bitLoc >> 3]) >> (8 - (bitLoc & 7) - bps)) & amask;
bitLoc += bps;
}
}
/* If no alpha, set it to opaque and increment spp. */
/* Otherwise, compute the true color values (by un-premultipling). */
if (![image hasAlpha])
{
data[spp] = amask;
spp++;
}
else if (data[spp - 1] && data[spp - 1] != amask)
{
for (sampleCnt = 0; sampleCnt < spp - 1; sampleCnt++)
{
data[sampleCnt] =
(unsigned char)(0.5 + amask *
( ((float)(data[sampleCnt])) / (data[spp - 1]) ));
}
}
/* At this point data[] contains spp samples, right justified */
/* within the range 0..amask. We can either return those, or */
/* return them in a normalized fashion (in the range 0..255, */
/* after shifting them over to the left by multiplying them by */
/* 255/amask), or we can return an NXColor. The latter is less */
/* efficient, probably, but most abstract. */
switch (colorSpace)
{
case NX_RGBColorSpace:
return NXConvertRGBAToColor(((float)data[0]) / amask,
((float)data[1]) / amask,
((float)data[2]) / amask,
[image hasAlpha] ? ((float)data[3]) / amask : NX_NOALPHA);
case NX_OneIsWhiteColorSpace:
return NXConvertGrayAlphaToColor(((float)data[0]) / amask,
[image hasAlpha] ? ((float)data[1]) / amask : NX_NOALPHA);
case NX_OneIsBlackColorSpace:
return NXConvertGrayAlphaToColor(((float)(amask - data[0])) / amask,
[image hasAlpha] ? ((float)(amask - data[1])) / amask :
NX_NOALPHA);
}
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.