/* Talin's Region Manager */ /* ===================================================================== * Includes * ===================================================================== */ #include "std.h" #include "rect.h" #include "regions.h" /* ===================================================================== * Internal classes * ===================================================================== */ // Used for iterating through rectangles in a strip class StripWalker { public: RegionRect *rect; int16 coord; int16 state; int16 final; StripWalker( RegionRect *list, int16 finalCoord ); void nextCoord( int16 minCoord ); }; // Used for iterating through strips in a region class RegionWalker { public: RegionStrip *strip; int16 y; bool nullStrip; int16 final; RegionStrip tempStrip; RegionRect tempRect; RegionWalker( const Region *reg, int16 yMin, int16 yMax ); ~RegionWalker() { tempStrip.rectList = NULL; } void nextStrip( int16 minCoord ); }; /* ===================================================================== * RegionRect member functions * ===================================================================== */ RegionRect::RegionRect() { next = NULL; } /* ===================================================================== * RegionStrip member functions * ===================================================================== */ RegionStrip::RegionStrip() { next = NULL; rectList = lastRect = NULL; } RegionStrip::~RegionStrip() { RegionRect *rect; // Delete all strips in the region. while ((rect = rectList) != NULL) { rectList = rectList->next; delete( rect ); } } bool RegionStrip::stripsEqual( RegionStrip &strip ) { RegionRect *rect1, *rect2; for (rect1 = rectList, rect2 = strip.rectList; rect1; rect1 = rect1->next, rect2 = rect2->next ) { if (rect2 == NULL || rect1->x != rect2->x || rect1->width != rect2->width) return FALSE; } if (rect2) return FALSE; return TRUE; } // Add a rectangle to a strip... void RegionStrip::addRect( int16 xPos, int16 width ) { RegionRect *rr; // If new rectangle overlaps the previous rectangle, // Then simply add to the width of that rectangle. if (lastRect) { if (xPos < lastRect->x) { printf( "Strip integrity error.\n" ); exit( 0 ); } if (xPos <= lastRect->x + lastRect->width) { lastRect->width = max( lastRect->width, xPos + width - lastRect->x ); return; } } // Otherwise, allocate a new rectangle rr = new RegionRect; rr->x = xPos; rr->width = width; if (lastRect) lastRect->next = rr; else rectList = rr; lastRect = rr; } RegionStrip *RegionStrip::copy( int16 y, int16 h ) { RegionRect *rect; RegionStrip *newStrip; newStrip = new RegionStrip; if (newStrip == NULL) return NULL; newStrip->yPos = y; newStrip->height = h; // Walk the list of rectangles and make a copy of each one for (rect = rectList; rect; rect = rect->next ) { RegionRect *rr; rr = new RegionRect; rr->x = rect->x; rr->width = rect->width; if (newStrip->lastRect) newStrip->lastRect->next = rr; else newStrip->rectList = rr; newStrip->lastRect = rr; } return newStrip; } // Return the left edge and width of entire strip. void RegionStrip::stripExtent( int16 &leftEdge, int16 &width ) { if (lastRect == NULL) { leftEdge = 0; width = 0; } else { leftEdge = rectList->x; width = lastRect->x + lastRect->width - leftEdge; } } // Do horiztonal boolean operation... // Note that this is a pipelined function, we don't store a rect // until the next rect changes RegionStrip *RegionStrip::combine( Rect16 stripExtent, // extent of strip RegionStrip &strip, // region strip to combine with int16 op ) // boolean operator { int16 outState; // output state: TRUE or FALSE int16 xPos = stripExtent.x, prevPos; // previous output coord // last x position of strip int16 finalPos = xPos + stripExtent.width; RegionStrip *newStrip; // allocated strip structure // Two StripWalkers to keep track of where we are in the strips. StripWalker sw1( rectList, finalPos ), sw2( strip.rectList, finalPos ); // Allocate an output strip newStrip = new RegionStrip; newStrip->yPos = stripExtent.y; newStrip->height = stripExtent.height; // Initialize the last procesed position. prevPos = xPos; // While xPos is less than the final position. while (xPos < finalPos) { // Choose the nearer of the two transitions, and insure // that it does not go beyond the final position. xPos = min( sw1.coord, sw2.coord ); if (xPos > finalPos) xPos = finalPos; // Compute the output state as a boolean function. outState = evalOp( op, sw1.state, sw2.state ); // If the output state is TRUE, then emit a RegionRect // to the new strip, with width equal to the distance // between the current point and the previously processed // point. if (outState && xPos > prevPos) newStrip->addRect( prevPos, xPos - prevPos ); // Update the previous position to the newly written one. prevPos = xPos; // Update both strips to beyond the last processed point. sw1.nextCoord( xPos ); sw2.nextCoord( xPos ); } if (newStrip->rectList == NULL) { delete( newStrip ); return NULL; } return newStrip; } /* ===================================================================== * StripWalker member functions * ===================================================================== */ StripWalker::StripWalker( RegionRect *list, int16 finalCoord ) { final = finalCoord; rect = list; coord = rect ? rect->x : final; state = 0; } void StripWalker::nextCoord( int16 minCoord ) { if (rect == NULL) // if past end of strip { coord = final; // then set coord to xMax state = 0; // set state to "between rects" return; } while (coord <= minCoord) { if (state) // if within a rect { state = 0; // set state to "between rects" rect = rect->next; // skip to next rect if (rect) { coord = rect->x; // set coord to rect start } else { coord = final; return; } } else // else if between rects { state = 1; // set state to "within rect" coord = rect->x + rect->width; // set coord to rect end } } } /* ===================================================================== * RegionWalker member functions * ===================================================================== */ RegionWalker::RegionWalker( const Region *reg, int16 yMin, int16 yMax ) { final = yMax; if (reg->flags & Region::empty) { strip = NULL; nullStrip = 1; y = yMax; return; } if (reg->flags & Region::simpleRect) { tempStrip.next = NULL; tempStrip.yPos = reg->bounds.y; tempStrip.height = reg->bounds.height; tempStrip.rectList = tempStrip.lastRect = &tempRect; tempRect.x = reg->bounds.x; tempRect.width = reg->bounds.width; tempRect.next = NULL; strip = &tempStrip; } else { strip = reg->stripList; } if (strip->yPos > yMin) { nullStrip = 1; y = strip->yPos; } else { nullStrip = 0; y = strip->yPos + strip->height; } } void RegionWalker::nextStrip( int16 minCoord ) { while (strip && strip->yPos + strip->height <= minCoord) { strip = strip->next; } if (strip == NULL) { y = final; nullStrip = 1; } else if (strip->yPos > minCoord) { y = strip->yPos; nullStrip = 1; } else { y = strip->yPos + strip->height; nullStrip = (strip->rectList) ? 0 : 1; } } /* ===================================================================== * Region member functions * ===================================================================== */ // Constructor and destructor Region::Region() { flags = empty | simpleRect; stripList = NULL; } Region::~Region() { clearRegion(); } #if 0 Region::Region( Region &rgn, Point16 &pt ) { flags = rgn.flags; bounds = rgn.bounds + pt; // now duplicate & shift strips RegionStrip *oldStrip = rgn.stripList, **newStrip = &stripList; stripList = NULL; while (oldStrip) { *newStrip = oldStrip->copy( oldStrip->yPos + pt.y, oldStrip->height); if (pt.x) { // Walk the list of rectangles and shift each one for (RegionRect *rect = (*newStrip)->rectList; rect; rect = rect->next ) { rect->x += pt.x; } } } } #endif void Region::extractRegion( Region &rgn ) { rgn.flags = flags; rgn.bounds = bounds; rgn.stripList = stripList; stripList = NULL; flags = empty | simpleRect; } void Region::clearRegion() { RegionStrip *strip; // Delete all strips in the region. while ((strip = stripList) != NULL) { stripList = stripList->next; delete( strip ); } stripList = NULL; flags = empty | simpleRect; } // Here's where all the real work is done. Another pipelined function, // meaning that we don't emit a strip until the succeeding strip is // finished. void Region::boolOpRegion( const Region &rgn, int16 op ) { Rect16 r; int16 stripTop, // previous output coord yPos, finalPos; // last x position of strip RegionStrip *firstStrip = NULL, *lastStrip = NULL; int16 xMin = maxint16, xMax = minint16; // Assume for a start that the new region's bounds are the // combined extent of both regions. r = ::bound( bounds, rgn.bounds ); RegionWalker rw1( this, r.y, r.y + r.height ), rw2( &rgn, r.y, r.y + r.height ); // Compute the starting and ending points of the region walk yPos = stripTop = r.y; finalPos = yPos + r.height; // Loop through all the strips. while (yPos < finalPos) { // Build a strip that represents the strip of pixels between // the last processed scanline, and the nearest end-of-strip. // Clip the y position to the bottom of the region bounds. yPos = min( rw1.y, rw2.y ); if (yPos > finalPos) yPos = finalPos; // If the strip we're making is of >0 height if (yPos > stripTop) { RegionStrip *newStrip; int16 h = yPos - stripTop; if (rw1.nullStrip) { // If the first strip is empty, check the OP // to see whether to copy or ignore the other // strip if (evalOp( op, 0, !rw2.nullStrip )) { newStrip = rw2.strip->copy( stripTop, h ); } else newStrip = NULL; } else if (rw2.nullStrip) { // If the second strip is empty, check the OP // to see whether to copy or ignore the other // strip if (evalOp( op, 1, 0 )) { newStrip = rw1.strip->copy( stripTop, h ); } else newStrip = NULL; } else { // Create a new strip by combining the old ones. newStrip = rw1.strip->combine( Rect16( r.x, stripTop, r.width, h ), *rw2.strip, op ); } if (newStrip) { // Check to see if the new strip is identical in form // to the last one generated. If so, then fold it // into the the previous strip. if ( lastStrip && stripTop <= lastStrip->yPos + lastStrip->height && lastStrip->stripsEqual( *newStrip )) { lastStrip->height = yPos - lastStrip->yPos; delete( newStrip ); } else { int16 leftEdge, width; // Get the extent of the strip, for later newStrip->stripExtent( leftEdge, width ); xMin = min( leftEdge, xMin ); xMax = max( leftEdge + width, xMax ); // Link the strip into the strip list if (lastStrip) lastStrip->next = newStrip; else firstStrip = newStrip; lastStrip = newStrip; } } } stripTop = yPos; rw1.nextStrip( yPos ); rw2.nextStrip( yPos ); } // delete old strips in destination region clearRegion(); if (firstStrip == NULL) { // If new region contains no strips bounds = r; } else if ( firstStrip == lastStrip && firstStrip->rectList == firstStrip->lastRect) { // If region only contains a single rectangle bounds.x = firstStrip->rectList->x; bounds.width = firstStrip->rectList->width; bounds.y = firstStrip->yPos; bounds.height = firstStrip->height; flags = simpleRect; delete( firstStrip ); } else { bounds.x = xMin; bounds.y = firstStrip->yPos; bounds.width = xMax - xMin; bounds.height = lastStrip->yPos + lastStrip->height - firstStrip->yPos; stripList = firstStrip; flags = 0; } } void Region::boolOpRect( const Rect16 &r, int16 op ) { if (flags & empty) // if region is empty { if (op & 2) // check "empty" op rect { bounds = r; // set region to rectangle flags = simpleRect; } } else { Region tempReg; tempReg.bounds = r; tempReg.flags = simpleRect; boolOpRegion( tempReg, op ); } } void Region::addRect( const Rect16 &r ) { boolOpRect( r, regOpOr ); } void Region::addRegion( const Region ® ) { boolOpRegion( reg, regOpOr ); } void Region::subtractRect( const Rect16 &r ) { boolOpRect( r, regOpAndInverted ); } void Region::subtractRegion( const Region ® ) { boolOpRegion( reg, regOpAndInverted ); } /* ===================================================================== * RegionIterator member functions * ===================================================================== */ RegionIterator::RegionIterator( const Region *reg, const Rect16 &search ) { region = reg; searchRect = search; rect = NULL; if (reg->flags & Region::simpleRect) { simpleFlag = TRUE; strip = NULL; } else { simpleFlag = FALSE; strip = reg->stripList; } } bool RegionIterator::next( Rect16 &clipRect ) { for (;;) { if (rect == NULL) { if (strip == NULL) { if (simpleFlag) { clipRect = region->bounds; simpleFlag = FALSE; return TRUE; } else return FALSE; } rect = strip->rectList; stripYPos = strip->yPos; stripHeight = strip->height; strip = strip->next; } else { clipRect.x = rect->x; clipRect.y = stripYPos; clipRect.width = rect->width; clipRect.height = stripHeight; rect = rect->next; if (clipRect.overlap( searchRect )) break; } } return TRUE; } #if 0 // Test code... #include #include #include //#include #include "input.h" #include "vdraw.h" #include "vsdraw.h" #include "gpalette.h" #include "pointer.h" bool appRunning; // true while app is running // Mouse Pointer gDisplayPort mainPort; gMousePointer pointer( mainPort ); // the actual pointer gPen mainPens[ cStdPenCount ],// the array of pens requesterPens[ cStdPenCount ];// the array of pens gPaletteEntry penColors[ cStdPenCount ] = { // the ideal pen colors 0,0,0, // black (transparent) 0,0,0, // black 255,255,255, // white 139,139,139, // dk. grey 191,191,191, // lt. grey 96, 96, 96, // dk. grey 191,191,191, // lt. grey 255,255,255, // white 139,139,139, // dk. grey 159,159,235, // lt. blue }; // A font for testing purposes extern "C" { extern gFont agateFont; }; gFont *mainFont = &agateFont; // main font (agate) extern gPalette mainPalette; // default color palette extern gMappedImage arrowPtr; void cleanup(); void showRegion( Region ® ); void main() { Region reg1, reg2; atexit( cleanup ); initSVGAGraphics( mainPort ); // init graphics initPanels( mainPort ); // init panel controls mainPort.setFont( mainFont ); pointer.hide(); pointer.setImage( arrowPtr, 0, 0 ); // Set the mouse pointer image pointer.show(); reg1.addRect( Rect16( 10, 10, 200, 100 ) ); reg1.addRect( Rect16( 50, 60, 200, 100 ) ); showRegion( reg1 ); getchar(); reg1.subtractRect( Rect16( 100, 70, 50, 150 ) ); showRegion( reg1 ); getchar(); reg1.addRect( Rect16( 100, 70, 50, 150 ) ); showRegion( reg1 ); getchar(); reg1.addRect( Rect16( 300, 240, 300, 200 ) ); showRegion( reg1 ); getchar(); reg1.subtractRect( Rect16( 400, 290, 100, 100 ) ); showRegion( reg1 ); getchar(); appRunning = TRUE; // EventLoop( appRunning ); // run the main loop exit( EXIT_SUCCESS ); } bool in_cleanup = FALSE; void cleanup() { // This prevents any functions in the cleanup from calling // exit() and thereby causing an infinite loop in_cleanup = TRUE; cleanupPanels(); cleanupSVGAGraphics(); } void ApplicationTask() { } void showRegion( Region ® ) { Rect16 r; RegionIterator iter( ®, Rect16( 0, 0, 640, 480 ) ); mainPort.setColor( 2 ); mainPort.clear(); // r = reg.getBounds(); // mainPort.setColor( 49 ); // mainPort.frameRect( r, 1 ); while (iter.next( r )) { mainPort.setColor( 0 ); mainPort.frameRect( r, 1 ); r.expand( -1, -1 ); mainPort.setColor( 48 ); mainPort.frameRect( r, 2 ); } } #endif