This is DirtPile.m in view mode; [Download] [Up]
// This object tracks the dirty rectangle of the frame buffer. // If two rects overlap, they are coalesced. If they don't // overlap, they are kept separate. This way, when a redraw // is done, we have two flushes that are small rather than one // large flush. (The large flush would draw *lots* of unnecessary // pixels!) #import <gamekit/gamekit.h> #import <appkit/appkit.h> #import <dpsclient/dpsNeXT.h> #import <stdio.h> // function to coalesce two rectangles if they overlap. // Returns NO if they don't, returns YES if they do and // leaves the union rect in *r1. I suppose that I could // have used NXIntersectsRect() and NXUnionRect(), but // this is just as easy. BOOL coalesce(NXRect *r1, NXRect *r2) { NXRect r3; // see if the two rects intersect. If they don't, return NO. if (((NX_X(r1) + NX_WIDTH(r1)) < NX_X(r2)) || ((NX_X(r2) + NX_WIDTH(r2)) < NX_X(r1)) || ((NX_Y(r1) + NX_HEIGHT(r1)) < NX_Y(r2)) || ((NX_Y(r2) + NX_HEIGHT(r2)) < NX_Y(r1))) return NO; // take the union of the rects; the new, larger rect is in r3 NX_X(&r3) = MIN(NX_X(r1), NX_X(r2)); NX_Y(&r3) = MIN(NX_Y(r1), NX_Y(r2)); NX_WIDTH(&r3) = MAX(NX_X(r1) + NX_WIDTH(r1), NX_X(r2) + NX_WIDTH(r2)) - NX_X(&r3); NX_HEIGHT(&r3) = MAX(NX_Y(r1) + NX_HEIGHT(r1), NX_Y(r2) + NX_HEIGHT(r2)) - NX_Y(&r3); *r1 = r3; return YES; // tell them we coalesced it. } @implementation DirtPile - init // initialize the instance { [super init]; // reset instance variables. maxRects = MAX_RECTS; numRects = 0; return self; } - addRegion:(float)x :(float)y :(float)w :(float)h // add a dirty rect { register NXRect *r; // make sure we have enough room. Complain if we don't. (Shouldn't // ever get the printf(), though! ) if (numRects >= maxRects) { fprintf(stderr, "Need more rects allocated in DirtPile!\n"); return self; } // copy coords into the rect list r = &rectList[numRects]; NX_X(r) = x; NX_Y(r) = y; NX_WIDTH(r) = w; NX_HEIGHT(r) = h; // update number of rects in list numRects++; return self; } - addRegion:(NXRect *)rect // add a dirty rect { return [self addRegion:NX_X(rect) :NX_Y(rect) :NX_WIDTH(rect) :NX_HEIGHT(rect)]; } - fullRedraw:sender :buffer // assumed to be a view { NXRect bounds = {{0.0, 0.0}, {0.0, 0.0}}; [sender getBounds:&bounds]; [buffer composite:NX_COPY fromRect:&bounds toPoint:&(bounds.origin)]; numRects = 0; return self; } // Flush dirty parts of the buffer to the currently focused view. // The view should be in a retained window for this to actually // work as it is supposed to do. - doRedraw:buffer { register NXRect *r1, *r2; register int i, j; BOOL changed; // Leave if nothing to flush. This isn't really necessary, since if // this object is properly used, there will be some dirt when this // method gets called. if (!numRects) return self; r1 = &rectList[0]; // not necessary, but silences an irrelevant // compiler warning. You could remove it, though, if it bothers you. // first, coaselse all redraw regions. do { changed = NO; for (i=0; i<(numRects-1); i++) { r1 = &rectList[i]; for (j=i+1; j<numRects; j++) { r2 = &rectList[j]; if (coalesce(r1, r2)) { changed = YES; numRects--; for (; j<numRects; j++) { *r2 = *(r2+1); r2++; } } } } } while (changed); // flush dirty rects from buffer onto the screen for (i=0; i<numRects; i++) { [buffer composite:NX_COPY fromRect:&rectList[i] toPoint:&rectList[i].origin]; // if using a buffered window instead of retained, you // need to flush the window here. } // reset dirty list, since it's all clean now numRects = 0; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.