This is CacheManager.m in view mode; [Download] [Up]
#import "CacheManager.h"
#import "ActorMgr.h"
#define XDEBUG 0
@implementation CacheManager
- eraseCache
{
NXRect r = {{0,0}};
NXSize theSize;
[cache getSize:&theSize];
r.size = theSize;
if ([cache lockFocus])
{
PSsetgray(NX_BLACK);
NXRectFill(&r);
if (virgin) [self tileUsing:tile];
[virgin composite:NX_COPY toPoint:&r.origin];
[cache unlockFocus];
}
return self;
}
- newSize:(NXSize *)sp
{
[cache free];
cache = [[NXImage allocFromZone:[self zone]] initSize:sp];
if (virgin)
{
[virgin free];
virgin = [[NXImage allocFromZone:[self zone]] initSize:sp];
}
[self eraseCache];
[eraseRectList empty];
return self;
}
- init
{
[super init];
displayList = [[List allocFromZone:[self zone]] init];
drawRectList = [[Storage allocFromZone:[self zone]]
initCount:8
elementSize: sizeof(NXRect)
description: @encode(NXRect)];
eraseRectList = [[Storage allocFromZone:[self zone]]
initCount:8
elementSize: sizeof(NXRect)
description: @encode(NXRect)];
return self;
}
// Coalesces 2 rectangles into 1 if they intersect, so that things
// are drawn without flickering and we send as few postscript messages
// as possible while keeping our redraw areas small. Does nothing
// and returns NO if the regions don't intersect, otherwise the combined
// region is returned in *p1 and returns YES
BOOL coalesce(NXRect *p1, NXRect *p2)
{
NXRect p3;
if (((p1->origin.x + p1->size.width) < p2->origin.x) ||
((p2->origin.x + p2->size.width) < p1->origin.x) ||
((p1->origin.y + p1->size.height) < p2->origin.y) ||
((p2->origin.y + p2->size.height) < p1->origin.y)) return NO;
p3.origin.x = MIN(p1->origin.x,p2->origin.x);
p3.origin.y = MIN(p1->origin.y,p2->origin.y);
p3.size.width = MAX(p1->origin.x+p1->size.width,
p2->origin.x+p2->size.width);
p3.size.width -= p3.origin.x;
p3.size.height = MAX(p1->origin.y+p1->size.height,
p2->origin.y+p2->size.height);
p3.size.height -= p3.origin.y;
// only coalesce if the resultant area is less than the sum of
// (the two input areas plus the simulated cost of an extra blit)
if ((p3.size.width * p3.size.height) >
(p1->size.width * p1->size.height +
p2->size.width * p2->size.height + (50.0 * 50.0))) return NO;
*p1 = p3;
return YES;
}
- oneStep
{
NXRect *p1, *p2, *rectArray;
int i, j, iterations;
BOOL changed;
int count;
Actor *theActor;
if ([cache lockFocus])
{
// first handle all cache erasures
if (eraseRectList->numElements)
{
if (!virgin)
{
PSsetgray(NX_BLACK);
NXRectFillList(eraseRectList->dataPtr, eraseRectList->numElements);
}
else
{
NXRect *r = (NXRect *) eraseRectList->dataPtr;
for (i=0; i<eraseRectList->numElements; i++)
[virgin composite:NX_COPY fromRect:r+i toPoint:&((r+i)->origin)];
}
[eraseRectList empty];
}
count = [displayList count];
// now construct next frame in the cache
for (i=0; i<count; i++)
{
theActor = (Actor *)[displayList objectAt:i];
// while I'm here, store all the rects that need flushing
// [theActor addFlushRectsTo:drawRectList];
[theActor draw];
}
[cache unlockFocus];
}
// coalesce some redraw regions
rectArray = drawRectList->dataPtr;
count = [drawRectList count];
iterations = 0;
do {
changed = NO;
for (i=0; i<(count-1); i++)
{
p1 = &rectArray[i];
if (p1->size.width <=0) continue;
for (j=i+1; j<count; j++)
{
p2 = &rectArray[j];
if (p2->size.width <=0) continue;
if (coalesce(p1,p2))
{
changed = YES;
p2->size.width = -1;
}
}
}
// } while (changed && (++iterations < 4));
} while (changed && (++iterations < 3));
for (i=0; i<count; i++)
{
if (rectArray[i].size.width > 0)
{
[cache composite:NX_COPY fromRect:&rectArray[i]
toPoint:&rectArray[i].origin];
#if XDEBUG
{
NXRect t = rectArray[i];
PSsetrgbcolor(.2,.2,1);
NXFrameRect(&t);
}
#endif
}
}
[drawRectList empty];
[displayList empty];
return self;
}
- erase:(NXRect *)r
{
[eraseRectList addElement:r];
// [drawRectList addElement:r];
return self;
}
- displayRect:(NXRect *)r
{
[drawRectList addElement:r];
return self;
}
- draw:(Actor *)sender;
{
[displayList addObject:sender];
return self;
}
- setBackground:(BOOL)val
{
NXSize theSize;
if (val)
{
[cache getSize:&theSize];
if (!virgin) virgin = [[NXImage allocFromZone:[self zone]] initSize:&theSize];
}
else
{
[virgin free];
tile = virgin = nil;
}
return self;
}
- background
{
return virgin;
}
- tileUsing:theTile
{
NXSize tileSize;
NXSize virginSize;
NXPoint pt;
[self setBackground:YES];
if (!theTile) return nil;
tile = theTile;
[theTile getSize:&tileSize];
[virgin getSize:&virginSize];
if ([virgin lockFocus])
{
for (pt.y = 0.0; pt.y < virginSize.height; pt.y += tileSize.height)
{
for (pt.x = 0.0; pt.x < virginSize.width; pt.x += tileSize.width)
{
[theTile composite:NX_SOVER toPoint:&pt];
}
}
[actorMgr makeActorsPerform:@selector(tile)];
[virgin unlockFocus];
}
return self;
}
- retileRect:(NXRect *)rp
{
NXSize tileSize;
NXSize virginSize;
NXPoint pt;
NXPoint edge;
NXRect src;
if (!tile) return nil;
[tile getSize:&tileSize];
[virgin getSize:&virginSize];
edge.x = rp->origin.x + rp->size.width;
edge.y = rp->origin.y + rp->size.height;
src.origin.y = (int)rp->origin.y % (int)tileSize.height;
src.size.height = MIN((tileSize.height-src.origin.y),rp->size.height);
if ([virgin lockFocus])
{
for (pt.y = rp->origin.y; pt.y < edge.y;)
{
src.origin.x = (int)rp->origin.x % (int)tileSize.width;
src.size.width = MIN((tileSize.width-src.origin.x),rp->size.width);
for (pt.x = rp->origin.x; pt.x < edge.x;)
{
[tile composite:NX_SOVER fromRect:&src toPoint:&pt];
pt.x += src.size.width;
src.origin.x = 0.0;
src.size.width = MIN((tileSize.width),edge.x-pt.x);
}
pt.y += src.size.height;
src.origin.y = 0.0;
src.size.height = MIN((tileSize.height),edge.y-pt.y);
}
[virgin unlockFocus];
}
return self;
}
- draw
{
NXPoint p = {0,0};
[virgin composite:NX_COPY toPoint:&p];
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.