USGS

Isis 3.0 Object Programmers' Reference

Home

ViewportBuffer.cpp

00001 #include "IsisDebug.h"
00002 
00003 #include "ViewportBuffer.h"
00004 #include "ViewportBufferAction.h"
00005 #include "ViewportBufferStretch.h"
00006 #include "ViewportBufferFill.h"
00007 #include "ViewportBufferTransform.h"
00008 
00009 #include <QApplication>
00010 #include <QQueue>
00011 #include <QRect>
00012 #include <QScrollBar>
00013 
00014 #include "Brick.h"
00015 #include "CubeDataThread.h"
00016 #include "CubeViewport.h"
00017 #include "SpecialPixel.h"
00018 #include "PixelType.h"
00019 
00020 
00021 #define round(x) ((x) > 0.0 ? (x) + 0.5 : (x) - 0.5)
00022 
00023 
00024 using namespace std;
00025 
00026 
00027 namespace Isis {
00036   ViewportBuffer::ViewportBuffer(CubeViewport *viewport,
00037                                  CubeDataThread *cubeData,
00038                                  int cubeId) {
00039     p_dataThread = cubeData;
00040     p_cubeId = cubeId;
00041 
00042     p_actions = 0;
00043 
00044     p_actions = new QQueue< ViewportBufferAction * >();
00045     p_viewport = viewport;
00046     p_bufferInitialized = false;
00047     p_band = -1;
00048     p_enabled = true;
00049     p_initialStretchDone = false;
00050     p_viewportHeight = p_viewport->viewport()->height();
00051     p_oldViewportHeight = p_viewport->viewport()->height();
00052     p_vertScrollBarPos = p_viewport->verticalScrollBar()->value();
00053     p_oldVertScrollBarPos = p_viewport->verticalScrollBar()->value();
00054 
00055     p_requestedFillArea = 0.0;
00056     p_bricksOrdered = true;
00057 
00058     connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
00059             p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
00060 
00061     connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
00062             this, SLOT(DataReady(void *, int, const Isis::Brick *)));
00063 
00064     connect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
00065             p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
00066   }
00067 
00072   ViewportBuffer::~ViewportBuffer() {
00073     disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
00074                p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
00075 
00076     disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
00077                this, SLOT(DataReady(void *, int, const Isis::Brick *)));
00078 
00079     disconnect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
00080                p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
00081 
00082     p_dataThread = NULL;
00083 
00084     if(p_actions) {
00085       while(!p_actions->empty()) {
00086         ViewportBufferAction *action = p_actions->dequeue();
00087         if(action) {
00088           delete action;
00089           action = NULL;
00090         }
00091       }
00092       delete p_actions;
00093       p_actions = NULL;
00094     }
00095 
00096     emptyBuffer(true);
00097   }
00098 
00099 
00106   void ViewportBuffer::fillBuffer(QRect rect) {
00107     if(p_band == -1) {
00108       throw IException(IException::Programmer, "invalid band", _FILEINFO_);
00109     }
00110 
00111     ViewportBufferFill *newFill = createViewportBufferFill(
00112                                     rect.intersected(bufferXYRect()), false);
00113     enqueueAction(newFill);
00114     doQueuedActions();
00115   }
00116 
00117 
00125   void ViewportBuffer::fillBuffer(QRect rect, const Brick *data) {
00126     if(p_band == -1) {
00127       throw IException(IException::Programmer, "invalid band", _FILEINFO_);
00128     }
00129 
00130     rect = rect.intersected(bufferXYRect());
00131 
00132     if (!rect.isValid())
00133       return;
00134 
00135     try {
00136       ViewportBufferFill *fill = createViewportBufferFill(rect, false);
00137 
00138       while(fill->shouldRequestMore()) {
00139         fill->incRequestPosition();
00140         fill->incReadPosition();
00141 
00142         for(int x = rect.left(); x <= rect.right(); x ++) {
00143           // Index into internal buffer is minus leftmost/topmost pixel
00144           int xIndex = x - fill->getLeftmostPixelPosition();
00145           int yIndex = fill->getRequestPosition() -
00146           fill->getTopmostPixelPosition();
00147 
00148           double samp = fill->viewportToSample(x);
00149           double line = fill->viewportToLine(fill->getRequestPosition());
00150           if (samp < data->Sample())
00151             samp = data->Sample();
00152           if (samp > data->Sample() + data->SampleDimension())
00153             samp = data->Sample() + data->SampleDimension();
00154           if (line < data->Line())
00155             line = data->Line();
00156           if (line > data->Line() + data->LineDimension())
00157             line = data->Line() + data->LineDimension();
00158 
00159           // Index into buffer is current sample - start sample
00160             //   *Brick indices are in units of cube pixels, not screen pixels
00161             int brickIndex = data->Index((int)(samp + 0.5), (int)(line + 0.5),
00162                                          p_band);
00163 
00164             if(brickIndex < 0) {
00165               p_buffer.at(yIndex).at(xIndex) = data->at(0);
00166             }
00167             else if(brickIndex >= data->size()) {
00168               p_buffer.at(yIndex).at(xIndex) = data->at(data->size() - 1);
00169             }
00170             else {
00171               if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
00172                 xIndex >= (int) p_buffer.at(yIndex).size()) {
00173                 throw IException(IException::Programmer,
00174                                  "index out of range",
00175                                  _FILEINFO_);
00176                 }
00177                 else {
00178                   p_buffer.at(yIndex).at(xIndex) = data->at(brickIndex);
00179                 }
00180             }
00181         }
00182       }
00183     }
00184     catch (IException &e) {
00185       throw IException(e, IException::Programmer, "Failed to load brick "
00186                        "into buffer", _FILEINFO_);
00187     }
00188   }
00189 
00190 
00200   void ViewportBuffer::DataReady(void *requester, int cubeId,
00201                                  const Brick *brick) {
00202     if(this != requester)
00203       return;
00204 
00205     if(p_actions->empty()) {
00206       throw IException(IException::Programmer, "no actions", _FILEINFO_);
00207     }
00208 
00209     ViewportBufferAction *curAction = p_actions->head();
00210 
00211     if(curAction->getActionType() != ViewportBufferAction::fill ||
00212         !curAction->started()) {
00213       throw IException(IException::Programmer, "not a fill action", _FILEINFO_);
00214     }
00215 
00216     ViewportBufferFill *fill = (ViewportBufferFill *) curAction;
00217 
00218     const QRect *rect = fill->getRect();
00219 
00220     int y = fill->getReadPosition(); // screen line
00221 
00222     // check to see if the next screen line's brick differs from this screen
00223     // line's brick.  If it does, which brick do we use?
00224     int curBrickLine = (int) (fill->viewportToLine(y) + 0.5);
00225     int nextBrickLine = (int) (fill->viewportToLine(y + 1) + 0.5);
00226     bool brickOrderCorrection = p_bricksOrdered;
00227     if (curBrickLine != nextBrickLine &&
00228         nextBrickLine == (int) (brick->Line() + 0.5)) {
00229       y++;
00230       p_bricksOrdered = false;
00231     }
00232     else {
00233       p_bricksOrdered = true;
00234     }
00235 
00236     double samp;
00237 
00238     // Loop through x values of rect on screen that we want to fill
00239     for(int x = rect->left(); x <= rect->right(); x++) {
00240       // Index into internal buffer is minus leftmost/topmost pixel
00241       int xIndex = x - fill->getLeftmostPixelPosition();
00242       int yIndex = y - fill->getTopmostPixelPosition();
00243 
00244       samp = fill->viewportToSample(x);
00245 
00246       // Index into buffer is current sample - start sample
00247       //   *Brick indices are in units of cube pixels, not screen pixels
00248       int brickIndex = (int)(samp + 0.5) - brick->Sample();
00249 
00250       if(brickIndex < 0) {
00251         p_buffer.at(yIndex).at(xIndex) = brick->at(0);
00252       }
00253       else if(brickIndex  >= brick->size()) {
00254         p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1);
00255       }
00256       else {
00257         if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
00258             xIndex >= (int) p_buffer.at(yIndex).size()) {
00259           IString msg = "An index out of range error was detected. ";
00260 
00261           if(yIndex < 0)
00262             msg += "The Y-Index [" + IString(yIndex) + "] is less than 0";
00263           else if(xIndex < 0)
00264             msg += "The X-Index [" + IString(xIndex) + "] is less than 0";
00265           else if(yIndex > (int)p_buffer.size())
00266             msg += "The Y-Index [" + IString(yIndex) + "] is greater than the "
00267                 "Y-Size of [" + IString((int)p_buffer.size()) + "]";
00268           else if(xIndex > (int)p_buffer.at(yIndex).size())
00269             msg += "The X-Index [" + IString(xIndex) + " is greater than the "
00270                 "X-Size of [" + IString((int) p_buffer.at(yIndex).size()) + "]";
00271 
00272           throw IException(IException::Programmer, msg, _FILEINFO_);
00273         }
00274         else {
00275           p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex);
00276         }
00277       }
00278     }
00279     fill->incReadPosition();
00280 
00281 
00282     if(fill->shouldRequestMore()) {
00283       if (p_bricksOrdered) {
00284         requestCubeLine(fill);
00285       }
00286       else {
00287         if (brickOrderCorrection) {
00288           requestCubeLine(fill);
00289           requestCubeLine(fill);
00290         }
00291       }
00292     }
00293     else if(fill->doneReading()) {
00294       delete fill;
00295       fill = NULL;
00296       p_actions->dequeue();
00297       doQueuedActions();
00298     }
00299 
00300     emit DoneWithData(cubeId, brick);
00301   }
00302 
00303 
00312   void ViewportBuffer::enqueueAction(ViewportBufferAction *action) {
00313     if(action->getActionType() == ViewportBufferAction::fill) {
00314       QRect *fillRect = ((ViewportBufferFill *)action)->getRect();
00315       p_requestedFillArea += fillRect->width() * fillRect->height();
00316     }
00317 
00318     if(p_actions->empty()) {
00319       p_viewport->enableProgress();
00320     }
00321 
00322     p_actions->enqueue(action);
00323   }
00324 
00325 
00334   const vector<double> &ViewportBuffer::getLine(int line) {
00335     if(!p_bufferInitialized || !p_enabled) {
00336       throw IException(IException::Programmer, "no data", _FILEINFO_);
00337     }
00338 
00339     if(line < 0 || line >= (int)p_buffer.size()) {
00340       throw IException(IException::Programmer,
00341                        "Invalid call to getLine",
00342                        _FILEINFO_);
00343     }
00344 
00345     return p_buffer.at(line);
00346   }
00347 
00348 
00356   QRect ViewportBuffer::getXYBoundingRect() {
00357     int startx, starty, endx, endy;
00358     p_viewport->cubeToViewport(0.5, 0.5, startx, starty);
00359 
00360     // Handle case where x,y 0,0 is sample,line 0,0 (which is outside the cube)
00361     //   and cubeToViewport still tells us 0.5, 0.5 is at x,y 0,0.
00362     double startSamp, startLine;
00363     p_viewport->viewportToCube(startx, starty, startSamp, startLine);
00364 
00365     if(startSamp < 0.5)
00366       startx ++;
00367 
00368     if(startLine < 0.5)
00369       starty ++;
00370 
00371     double rightmost = p_viewport->cubeSamples() + 0.5;
00372     double bottommost = p_viewport->cubeLines() + 0.5;
00373 
00374     p_viewport->cubeToViewport(rightmost, bottommost, endx, endy);
00375 
00376     if(endx < 0 || endy < 0)
00377       return QRect();
00378 
00379     double endSamp = -1, endLine = -1;
00380     p_viewport->viewportToCube(endx, endy, endSamp, endLine);
00381 
00382     if(endSamp > rightmost)
00383       endx --;
00384 
00385     if(endLine > bottommost)
00386       endy --;
00387 
00388     // Make sure our rect makes sense
00389     if(startx < 0) {
00390       startx = 0;
00391     }
00392 
00393     if(starty < 0) {
00394       starty = 0;
00395     }
00396 
00397     if(endx >= p_viewport->viewport()->width()) {
00398       endx = p_viewport->viewport()->width() - 1;
00399     }
00400 
00401     if(endy >= p_viewport->viewport()->height()) {
00402       endy = p_viewport->viewport()->height() - 1;
00403     }
00404 
00405     return QRect(startx, starty, endx - startx + 1, endy - starty + 1);
00406   }
00407 
00408 
00415   bool ViewportBuffer::hasEntireCube() {
00416     double sampTolerance = 0.05 * p_viewport->cubeSamples();
00417     double lineTolerance = 0.05 * p_viewport->cubeLines();
00418 
00419     bool hasCube = true;
00420 
00421     hasCube &= !working();
00422     hasCube &= p_sampLineBoundingRect[rectLeft] <= (1 + sampTolerance);
00423     hasCube &= p_sampLineBoundingRect[rectTop] <= (1 + lineTolerance);
00424     hasCube &= p_sampLineBoundingRect[rectRight] >= (p_viewport->cubeSamples() -
00425                sampTolerance);
00426     hasCube &= p_sampLineBoundingRect[rectBottom] >= (p_viewport->cubeLines() -
00427                lineTolerance);
00428     return hasCube;
00429   }
00430 
00431 
00439   QList<double> ViewportBuffer::getSampLineBoundingRect() {
00440     QRect xyRect = getXYBoundingRect();
00441     double ssamp, esamp, sline, eline;
00442     p_viewport->viewportToCube(xyRect.left(), xyRect.top(), ssamp, sline);
00443     p_viewport->viewportToCube(xyRect.right(), xyRect.bottom(), esamp, eline);
00444 
00445     QList<double> boundingRect;
00446 
00447     boundingRect.insert(rectLeft, ssamp);
00448     boundingRect.insert(rectTop, sline);
00449     boundingRect.insert(rectRight, esamp);
00450     boundingRect.insert(rectBottom, eline);
00451 
00452     return boundingRect;
00453   }
00454 
00455 
00460   void ViewportBuffer::updateBoundingRects() {
00461     p_oldXYBoundingRect = p_XYBoundingRect;
00462     p_XYBoundingRect = getXYBoundingRect();
00463 
00464     p_oldSampLineBoundingRect = p_sampLineBoundingRect;
00465     p_sampLineBoundingRect = getSampLineBoundingRect();
00466 
00467     p_oldViewportHeight = p_viewportHeight;
00468     p_viewportHeight = p_viewport->viewport()->height();
00469 
00470     p_oldVertScrollBarPos = p_vertScrollBarPos;
00471     // Add +1 to remove the black line at the top
00472     p_vertScrollBarPos = p_viewport->verticalScrollBar()->value() + 1;
00473   }
00474 
00475 
00488   ViewportBufferFill *ViewportBuffer::createViewportBufferFill(
00489     QRect someRect, bool useOldY) {
00490     QScrollBar *hsb = p_viewport->horizontalScrollBar();
00491     int xConstCoef = hsb->value();
00492     xConstCoef -= p_viewport->viewport()->width() / 2;
00493 
00494     // If panning over a full screen, it will try to create a fill rect that
00495     //   isn't actually valid. So, in the case of any bad fill rects we will
00496     //   fill everything.
00497     if(!someRect.isValid()) {
00498       throw IException(IException::Programmer, "Fill rect invalid", _FILEINFO_);
00499     }
00500 
00501     double xScale = p_viewport->scale();
00502 
00503     int yConstCoef = 0;
00504 
00505     if(!useOldY)
00506       yConstCoef = (p_vertScrollBarPos) - p_viewportHeight / 2 - 1;
00507     else
00508       yConstCoef = (p_oldVertScrollBarPos) - p_oldViewportHeight / 2 - 1;
00509 
00510     double yScale = xScale;
00511 
00512     QPoint topLeft;
00513 
00514     if(!useOldY) {
00515       topLeft = p_XYBoundingRect.topLeft();
00516     }
00517     else {
00518       topLeft = QPoint(p_XYBoundingRect.left(), p_oldXYBoundingRect.top());
00519     }
00520 
00521     ViewportBufferFill *newFill = new ViewportBufferFill(someRect, xConstCoef,
00522         xScale, yConstCoef, yScale, topLeft);
00523 
00524     return newFill;
00525   }
00526 
00527 
00533   void ViewportBuffer::requestCubeLine(ViewportBufferFill *fill) {
00534     if(p_band == -1) {
00535       throw IException(IException::Programmer, "invalid band", _FILEINFO_);
00536     }
00537 
00538     // Prep to create minimal buffer(s) to read the cube
00539     QRect &rect = *fill->getRect();
00540 
00541     double ssamp = fill->viewportToSample(rect.left());
00542 
00543     double esamp = fill->viewportToSample(rect.right());
00544 
00545     int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1;
00546 
00547     if(brickWidth <= 0)
00548       return;
00549 
00550     double line = fill->viewportToLine(fill->getRequestPosition());
00551     int roundedSamp = (int)(ssamp + 0.5);
00552     int roundedLine = (int)(line + 0.5);
00553 
00554     emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth,
00555                   roundedLine, p_band, this);
00556 
00557     fill->incRequestPosition();
00558   }
00559 
00560 
00571   void ViewportBuffer::doQueuedActions() {
00572     bool doNextAction = false;
00573 
00574     ViewportBufferAction *curAction = NULL;
00575 
00576     // if we aren't preserving data, and we don't still need the initial
00577     //   stretch (on startup), let's reset the buffer.
00578     if(!reinitializeActionExists() && !actionsPreserveData() &&
00579         p_initialStretchDone) {
00580       // Actions don't preserve data - call reinitialize!
00581       reinitialize();
00582     }
00583 
00584     if(!working()) {
00585       p_requestedFillArea = 0.0;
00586     }
00587 
00588     if(!p_actions->empty()) {
00589       curAction = p_actions->head();
00590       doNextAction = !curAction->started();
00591     }
00592 
00593     while(doNextAction) {
00594       if(curAction->getActionType() == ViewportBufferAction::transform) {
00595         doTransformAction((ViewportBufferTransform *) curAction);
00596       }
00597       else if(curAction->getActionType() == ViewportBufferAction::fill) {
00598         startFillAction((ViewportBufferFill *) curAction);
00599       }
00600       else {
00601         doStretchAction((ViewportBufferStretch *) curAction);
00602         p_initialStretchDone = true;
00603       }
00604 
00605       doNextAction = !p_actions->empty();
00606 
00607       if(doNextAction) {
00608         curAction = p_actions->head();
00609         doNextAction = !curAction->started();
00610       }
00611     }
00612 
00613     if(p_actions->empty()) {
00614       // Buffer Updated - Giving it BufferXYRect
00615       p_viewport->bufferUpdated(bufferXYRect());
00616     }
00617   }
00618 
00619 
00625   double ViewportBuffer::currentProgress() {
00626     if(!working())
00627       return 1.0;
00628     if(p_requestedFillArea <= 0.0)
00629       return 0.0;
00630 
00631     return 1.0 - totalUnfilledArea() / p_requestedFillArea;
00632   }
00633 
00634 
00641   double ViewportBuffer::totalUnfilledArea() {
00642     double totalFillArea = 0.0;
00643 
00644     // If at any time the total X or Y shift exceeds the buffer size, we aren't
00645     //   preserving data. Check to see if this is the case!
00646     for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
00647       ViewportBufferAction *action = (*p_actions)[actionIndex];
00648 
00649       if(action->getActionType() == ViewportBufferAction::fill) {
00650         ViewportBufferFill *fill = (ViewportBufferFill *)action;
00651 
00652         QRect unfilledRect(*fill->getRect());
00653         unfilledRect.setTop(fill->getReadPosition());
00654         totalFillArea += unfilledRect.width() * unfilledRect.height();
00655       }
00656     }
00657 
00658     return totalFillArea;
00659   }
00660 
00661 
00666   bool ViewportBuffer::actionsPreserveData() {
00667     int totalXShift = 0;
00668     int totalYShift = 0;
00669 
00670     QRect currentBufferRect(bufferXYRect());
00671 
00672     int bufferWidth  = currentBufferRect.width();
00673     int bufferHeight = currentBufferRect.height();
00674 
00675     // If at any time the total X or Y shift exceeds the buffer size, we aren't
00676     //   preserving data. Check to see if this is the case!
00677     for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
00678       ViewportBufferAction *action = (*p_actions)[actionIndex];
00679 
00680       if(action->getActionType() == ViewportBufferAction::transform) {
00681         ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
00682 
00683         if(transform->resizeFirst()) {
00684           bufferWidth = transform->getBufferWidth();
00685           bufferHeight = transform->getBufferHeight();
00686         }
00687 
00688         if(abs(totalXShift) >= bufferWidth)
00689           return false;
00690         if(abs(totalYShift) >= bufferHeight)
00691           return false;
00692 
00693         // Without the absolute value this will calculate
00694         //   if any data on the screen is preserved, however
00695         //   a better method is to see if its quicker to reread
00696         //   it all which happens when we use abs
00697         totalXShift += abs(transform->getXTranslation());
00698         totalYShift += abs(transform->getYTranslation());
00699 
00700         if(!transform->resizeFirst()) {
00701           bufferWidth = transform->getBufferWidth();
00702           bufferHeight = transform->getBufferHeight();
00703         }
00704 
00705         if(abs(totalXShift) >= bufferWidth)
00706           return false;
00707         if(abs(totalYShift) >= bufferHeight)
00708           return false;
00709       }
00710     }
00711 
00712     return true;
00713   }
00714 
00715 
00722   bool ViewportBuffer::reinitializeActionExists() {
00723     QRect currentBufferRect(bufferXYRect());
00724 
00725     if(currentBufferRect.width() == 0 || currentBufferRect.height() == 0) {
00726       return true;
00727     }
00728 
00729     for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
00730       ViewportBufferAction *action = (*p_actions)[actionIndex];
00731 
00732       if(action->getActionType() == ViewportBufferAction::transform) {
00733         ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
00734 
00735         if(transform->getBufferWidth()  == 0)
00736           return true;
00737         if(transform->getBufferHeight() == 0)
00738           return true;
00739       }
00740     }
00741 
00742     return false;
00743   }
00744 
00745 
00751   bool ViewportBuffer::working() {
00752     return !p_actions->empty() || !p_bufferInitialized || !p_enabled;
00753   }
00754 
00755 
00761   void ViewportBuffer::doTransformAction(ViewportBufferTransform *action) {
00762     bool needResize = action->getBufferWidth() > -1 &&
00763                       action->getBufferHeight() > -1;
00764 
00765     if(action->resizeFirst() && needResize) {
00766       resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
00767     }
00768 
00769     shiftBuffer(action->getXTranslation(), action->getYTranslation());
00770 
00771     if(!action->resizeFirst() && needResize) {
00772       resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
00773     }
00774 
00775     delete action;
00776     action = NULL;
00777     p_actions->dequeue();
00778   }
00779 
00780 
00787   void ViewportBuffer::startFillAction(ViewportBufferFill *action) {
00788     if(action->started())
00789       return;
00790 
00791     action->started(true);
00792 
00793     requestCubeLine(action);
00794 
00795     if(action->shouldRequestMore()) {
00796       requestCubeLine(action);
00797     }
00798   }
00799 
00800 
00806   void ViewportBuffer::doStretchAction(ViewportBufferStretch *action) {
00807     delete action;
00808     action = NULL;
00809     p_actions->dequeue();
00810 
00811     p_viewport->restretch(this);
00812   }
00813 
00814 
00821   void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
00822     p_buffer.resize(height);
00823 
00824     for(unsigned int i = 0; i < p_buffer.size(); i++) {
00825       p_buffer[i].resize(width, Null);
00826     }
00827   }
00828 
00829 
00837   void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
00838     if(deltaY >= 0) {
00839       for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
00840         p_buffer[i] = p_buffer[i - deltaY];
00841 
00842         // If we have y shift, null out original data (keep only moved data)
00843         if(deltaY != 0) {
00844           for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
00845             p_buffer[i - deltaY][x] = Null;
00846           }
00847         }
00848 
00849         if(deltaX > 0) {
00850           for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
00851             p_buffer[i][j] = p_buffer[i][j - deltaX];
00852             p_buffer[i][j - deltaX] = Null;
00853           }
00854         }
00855         else if(deltaX < 0) {
00856           for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
00857             p_buffer[i][j] = p_buffer[i][j - deltaX];
00858             p_buffer[i][j - deltaX] = Null;
00859           }
00860         }
00861       }
00862     }
00863     else if(deltaY < 0) {
00864       for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
00865         p_buffer[i] = p_buffer[i - deltaY];
00866 
00867         // null out original data (keep only moved data)
00868         for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
00869           p_buffer[i - deltaY][x] = Null;
00870         }
00871 
00872         if(deltaX > 0) {
00873           for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
00874             p_buffer[i][j] = p_buffer[i][j - deltaX];
00875             p_buffer[i][j - deltaX] = Null;
00876           }
00877         }
00878         else if(deltaX < 0) {
00879           for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
00880             p_buffer[i][j] = p_buffer[i][j - deltaX];
00881             p_buffer[i][j - deltaX] = Null;
00882           }
00883         }
00884       }
00885     }
00886   }
00887 
00888 
00893   void ViewportBuffer::resizedViewport() {
00894     updateBoundingRects();
00895 
00896     if(!p_bufferInitialized || !p_enabled)
00897       return;
00898 
00899     // ensure we have a valid bounding rect!  For example, If the cube viewport
00900     // is hidden and then shown again this could happen.
00901     if(!p_XYBoundingRect.isValid())
00902       return;
00903 
00904     if(!p_oldXYBoundingRect.isValid()) {
00905       reinitialize();
00906       return;
00907     }
00908 
00909     //We need to know how much data was gained/lost on each side of the cube
00910     double deltaLeftSamples =  p_sampLineBoundingRect[rectLeft] -
00911                                p_oldSampLineBoundingRect[rectLeft];
00912     //The input to round should be close to an integer
00913     int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
00914 
00915     double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
00916                                p_oldSampLineBoundingRect[rectRight];
00917     int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
00918 
00919     double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
00920                              p_oldSampLineBoundingRect[rectTop];
00921     int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
00922 
00923     double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
00924                                 p_oldSampLineBoundingRect[rectBottom];
00925     int deltaBottomPixels = (int)round(deltaBottomSamples *
00926                                        p_viewport->scale());
00927 
00928     //deltaW is the change in width in the visible area of the cube
00929     int deltaW = - deltaLeftPixels + deltaRightPixels;
00930 
00931     //deltaH is the change in height in the visible area of the cube
00932     int deltaH = - deltaTopPixels + deltaBottomPixels;
00933 
00934     //If the new visible width has changed (resized in the horizontal direction)
00935     if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
00936       //Resized larger in the horizontal direction
00937       if(deltaW > 0) {
00938         //Using old height because we might lose data if new height is smaller
00939         ViewportBufferTransform *transform = new ViewportBufferTransform();
00940         transform->setTranslation(-deltaLeftPixels, 0);
00941         transform->setResize(p_XYBoundingRect.width(),
00942                              p_oldXYBoundingRect.height());
00943         transform->resizeFirst(true);
00944 
00945         enqueueAction(transform);
00946 
00947         // left side that needs filled
00948         QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
00949                                  p_oldXYBoundingRect.top());
00950 
00951         QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
00952                                      p_oldXYBoundingRect.bottom());
00953 
00954         QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
00955 
00956         ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
00957                                        true);
00958         enqueueAction(leftFill);
00959 
00960         // right side that needs filled
00961         QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
00962                                   p_oldXYBoundingRect.top());
00963 
00964         QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
00965                                       p_oldXYBoundingRect.bottom());
00966 
00967         QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
00968 
00969         ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
00970                                         true);
00971         enqueueAction(rightFill);
00972       }
00973       //Resized smaller in the horizontal direction
00974       else if(deltaW < 0) {
00975         ViewportBufferTransform *transform = new ViewportBufferTransform();
00976         transform->setTranslation(-deltaLeftPixels, 0);
00977         transform->setResize(p_XYBoundingRect.width(),
00978                              p_oldXYBoundingRect.height());
00979         transform->resizeFirst(false);
00980         enqueueAction(transform);
00981       }
00982     }
00983 
00984     //If the new visible height has changed (resized in the vertical direction)
00985     if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
00986       //Resized larger in the vertical direction
00987       if(deltaH > 0) {
00988         ViewportBufferTransform *transform = new ViewportBufferTransform();
00989         transform->setTranslation(0, -deltaTopPixels);
00990         transform->setResize(p_XYBoundingRect.width(),
00991                              p_XYBoundingRect.height());
00992         transform->resizeFirst(true);
00993 
00994         enqueueAction(transform);
00995 
00996         QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
00997                                     p_XYBoundingRect.top() - deltaTopPixels);
00998 
00999         QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
01000 
01001 
01002         QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
01003                                    p_XYBoundingRect.bottom() -
01004                                    deltaBottomPixels);
01005 
01006         QRect bottomSideToFill(topLeftOfbottomSide,
01007                                p_XYBoundingRect.bottomRight());
01008 
01009         ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
01010                                       false);
01011         enqueueAction(topFill);
01012 
01013         ViewportBufferFill *bottomFill =
01014           createViewportBufferFill(bottomSideToFill, false);
01015         enqueueAction(bottomFill);
01016       }
01017       //Resized smaller in the vertical direction
01018       else if(deltaH < 0) {
01019         ViewportBufferTransform *transform = new ViewportBufferTransform();
01020 
01021         transform->setTranslation(0, -deltaTopPixels);
01022         transform->setResize(p_XYBoundingRect.width(),
01023                              p_oldXYBoundingRect.height());
01024 
01025         enqueueAction(transform);
01026       }
01027     }
01028 
01029     doQueuedActions();
01030   }
01031 
01032 
01040   void ViewportBuffer::pan(int deltaX, int deltaY) {
01041     updateBoundingRects();
01042 
01043     if(!p_bufferInitialized || !p_enabled) {
01044       return;
01045     }
01046 
01047 
01048     if(p_sampLineBoundingRect == p_oldSampLineBoundingRect) {
01049       //The sample line bounding rect contains the bounds of the
01050       //screen pixels to the cube sample/line bounds. If the cube
01051       //bounds do not change, then we do not need to do anything to
01052       //the buffer.
01053       return;
01054     }
01055 
01056     double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
01057                               p_oldSampLineBoundingRect[rectLeft];
01058     int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
01059 
01060     double deltaTopLines = p_sampLineBoundingRect[rectTop] -
01061                            p_oldSampLineBoundingRect[rectTop];
01062     int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
01063 
01064     // Don't try to figure out panning beyond a full screen,
01065     //   even though data could very well be preserved.
01066     if(abs(deltaY) >= p_XYBoundingRect.height() ||
01067         abs(deltaX) >= p_XYBoundingRect.width()) {
01068       reinitialize();
01069       return;
01070     }
01071 
01072     //Left side of the visible area changed (start sample is different)
01073     if(p_sampLineBoundingRect[rectLeft] != p_oldSampLineBoundingRect[rectLeft]) {
01074       //Shifting data to the right
01075       if(deltaX > 0) {
01076         // The buffer is getting bigger
01077         ViewportBufferTransform *transform = new ViewportBufferTransform();
01078         transform->setResize(p_XYBoundingRect.width(),
01079                              p_oldXYBoundingRect.height());
01080         transform->setTranslation(-deltaLeftPixels, 0);
01081         transform->resizeFirst(true);
01082 
01083         enqueueAction(transform);
01084 
01085         QPoint topLeftOfRefill(p_XYBoundingRect.left(),
01086                                p_oldXYBoundingRect.top());
01087 
01088         QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
01089                                    p_oldXYBoundingRect.bottom());
01090         QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
01091 
01092         ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
01093         enqueueAction(fill);
01094       }
01095       //Shifting data to the left
01096       else if(deltaX < 0) {
01097         //The buffer is getting smaller - no new data
01098         ViewportBufferTransform *transform = new ViewportBufferTransform();
01099         transform->setTranslation(-deltaLeftPixels, 0);
01100         transform->setResize(p_XYBoundingRect.width(),
01101                              p_oldXYBoundingRect.height());
01102         transform->resizeFirst(false);
01103         enqueueAction(transform);
01104 
01105         // if new samples on the screen at all, mark it for reading
01106         if(p_sampLineBoundingRect[rectRight] !=
01107             p_oldSampLineBoundingRect[rectRight]) {
01108           QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
01109                                  p_oldXYBoundingRect.top());
01110           QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
01111                                      p_oldXYBoundingRect.bottom());
01112 
01113           QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
01114 
01115           ViewportBufferFill *fill = createViewportBufferFill(refillArea,
01116                                      true);
01117           enqueueAction(fill);
01118         }
01119       }
01120     }
01121     // Left side of the visible area is the same (start sample has not changed,
01122     // but end sample may be different)
01123     else {
01124       ViewportBufferTransform *transform = new ViewportBufferTransform();
01125       transform->setResize(p_XYBoundingRect.width(),
01126                            p_oldXYBoundingRect.height());
01127       enqueueAction(transform);
01128 
01129       if(deltaX < 0) {
01130         QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
01131                                  p_oldXYBoundingRect.top());
01132 
01133         QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
01134                                      p_oldXYBoundingRect.bottom());
01135 
01136         QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
01137         ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
01138 
01139         enqueueAction(fill);
01140       }
01141     }
01142 
01143     //Top side of the visible area changed (start line is different)
01144     if(p_sampLineBoundingRect[rectTop] != p_oldSampLineBoundingRect[rectTop]) {
01145       //Shifting data down
01146       if(deltaY > 0) {
01147         ViewportBufferTransform *transform = new ViewportBufferTransform();
01148         transform->setTranslation(0, -deltaTopPixels);
01149         transform->setResize(p_XYBoundingRect.width(),
01150                              p_XYBoundingRect.height());
01151         transform->resizeFirst(true);
01152         enqueueAction(transform);
01153 
01154         QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
01155                                  p_XYBoundingRect.top());
01156         QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
01157                                      p_XYBoundingRect.top() + deltaY);
01158         QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
01159         ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
01160         enqueueAction(fill);
01161       }
01162       //Shifting data up
01163       else if(deltaY < 0) {
01164         ViewportBufferTransform *transform = new ViewportBufferTransform();
01165         transform->setTranslation(0, -deltaTopPixels);
01166         transform->setResize(p_XYBoundingRect.width(),
01167                              p_XYBoundingRect.height());
01168         transform->resizeFirst(false);
01169         enqueueAction(transform);
01170 
01171         // if new lines on the screen at all, mark it for reading
01172         if(p_sampLineBoundingRect[rectBottom] !=
01173             p_oldSampLineBoundingRect[rectBottom]) {
01174           QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
01175                                    p_oldXYBoundingRect.bottom() + deltaY);
01176           QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
01177                                        p_XYBoundingRect.bottom());
01178           QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
01179           ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
01180           enqueueAction(fill);
01181         }
01182       }
01183     }
01184     // Top side of the visible area is the same (start line has not changed, but
01185     // end line may be different)
01186     else {
01187       ViewportBufferTransform *transform = new ViewportBufferTransform();
01188       transform->setResize(p_XYBoundingRect.width(),
01189                            p_XYBoundingRect.height());
01190       enqueueAction(transform);
01191 
01192       if(deltaY < 0) {
01193         QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
01194                                  p_XYBoundingRect.bottom() + deltaY);
01195         QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
01196                                      p_XYBoundingRect.bottom());
01197         QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
01198         ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
01199         enqueueAction(fill);
01200       }
01201     }
01202 
01203     doQueuedActions();
01204   }
01205 
01206 
01212   void ViewportBuffer::addStretchAction() {
01213     for(int i = 0; i < p_actions->size(); i++) {
01214       if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
01215         p_actions->removeAt(i);
01216         i --;
01217       }
01218     }
01219 
01220     enqueueAction(new ViewportBufferStretch());
01221     doQueuedActions();
01222   }
01223 
01233   void ViewportBuffer::emptyBuffer(bool force) {
01234     if(force) {
01235       p_buffer.clear();
01236       p_bufferInitialized = false;
01237     }
01238   }
01239 
01240 
01249   QRect ViewportBuffer::bufferXYRect() {
01250     QRect rect = p_XYBoundingRect;
01251 
01252     if(!rect.height() || !p_buffer.size())
01253       return QRect();
01254 
01255     if(rect.height() > (int) p_buffer.size())
01256       rect.setBottom(rect.top() + p_buffer.size() - 1);
01257 
01258     if(rect.width() > (int) p_buffer[0].size())
01259       rect.setRight(rect.left() + p_buffer[0].size() - 1);
01260 
01261     return rect;
01262   }
01263 
01264 
01273   void ViewportBuffer::scaleChanged() {
01274     if(!p_enabled)
01275       return;
01276 
01277     try {
01278       updateBoundingRects();
01279       reinitialize();
01280     }
01281     catch (IException &e) {
01282       throw IException(
01283           e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
01284     }
01285   }
01286 
01287 
01294   void ViewportBuffer::enable(bool enabled) {
01295     bool wasEnabled = p_enabled;
01296 
01297     p_enabled = enabled;
01298 
01299     if(!wasEnabled && p_enabled) {
01300       updateBoundingRects();
01301       reinitialize();
01302     }
01303   }
01304 
01305 
01311   void ViewportBuffer::setBand(int band) {
01312     if(p_band == band)
01313       return;
01314     p_band = band;
01315 
01316     updateBoundingRects();
01317 
01318     if(!p_enabled)
01319       return;
01320 
01321     reinitialize();
01322   }
01323 
01324 
01332   void ViewportBuffer::reinitialize() {
01333 
01334     try {
01335       // If we're in the middle of a process, we got an okay stretch on startup,
01336       // then we can stop what we're doing.
01337       if(working() && p_initialStretchDone) {
01338         // We only need to handle the current action, can ignore others
01339         ViewportBufferAction *curAction = p_actions->head();
01340 
01341         // Delete older actions
01342         for(int i = p_actions->size() - 1; i > 0; i--) {
01343           delete(*p_actions)[i];
01344           p_actions->pop_back();
01345         }
01346 
01347         // Deal with current action
01348         if(curAction->started()) {
01349           if(curAction->getActionType() == ViewportBufferAction::fill) {
01350             ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
01351 
01352             fill->stop();
01353 
01354             p_requestedFillArea = fill->getRect()->height() *
01355                                   fill->getRect()->width();
01356           }
01357         }
01358         else {
01359           delete curAction;
01360           p_actions->clear();
01361           p_requestedFillArea = 0.0;
01362         }
01363       }
01364 
01365 
01366       p_bufferInitialized = true;
01367 
01368       ViewportBufferTransform *reset = new ViewportBufferTransform();
01369       reset->setResize(0, 0);
01370       enqueueAction(reset);
01371 
01372       if (p_XYBoundingRect.isValid()) {
01373         ViewportBufferTransform *transform = new ViewportBufferTransform();
01374         transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
01375         enqueueAction(transform);
01376         ViewportBufferFill *fill = createViewportBufferFill(p_XYBoundingRect,
01377                                    false);
01378         enqueueAction(fill);
01379       }
01380 
01381       doQueuedActions();
01382     }
01383     catch (IException &e) {
01384       throw IException(IException::Programmer,
01385                        "Unable to resize and fill buffer.",
01386                        _FILEINFO_);
01387     }
01388   }
01389 }