|
Isis 3.0 Object Programmers' Reference |
Home |
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 }