File failed to load: https://isis.astrogeology.usgs.gov/6.0.0/Object/assets/jax/output/NativeMML/config.js
Isis 3 Programmer Reference
ViewportBuffer.cpp
1 
7 /* SPDX-License-Identifier: CC0-1.0 */
8 
9 #include "IsisDebug.h"
10 
11 #include "ViewportBuffer.h"
12 #include "ViewportBufferAction.h"
13 #include "ViewportBufferStretch.h"
14 #include "ViewportBufferFill.h"
15 #include "ViewportBufferTransform.h"
16 
17 #include <QApplication>
18 #include <QQueue>
19 #include <QRect>
20 #include <QScrollBar>
21 
22 #include "Brick.h"
23 #include "CubeDataThread.h"
24 #include "CubeViewport.h"
25 #include "SpecialPixel.h"
26 #include "PixelType.h"
27 
28 
29 #define round(x) ((x) > 0.0 ? (x) + 0.5 : (x) - 0.5)
30 
31 
32 using namespace std;
33 
34 
35 namespace Isis {
44  ViewportBuffer::ViewportBuffer(CubeViewport *viewport,
45  CubeDataThread *cubeData,
46  int cubeId) {
47  p_dataThread = cubeData;
48  p_cubeId = cubeId;
49 
50  p_actions = 0;
51 
52  p_actions = new QQueue< ViewportBufferAction * >();
53  p_viewport = viewport;
54  p_bufferInitialized = false;
55  p_band = -1;
56  p_enabled = true;
57  p_initialStretchDone = false;
58  p_viewportHeight = p_viewport->viewport()->height();
59  p_oldViewportHeight = p_viewport->viewport()->height();
60  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value();
61  p_oldVertScrollBarPos = p_viewport->verticalScrollBar()->value();
62 
63  p_requestedFillArea = 0.0;
64  p_bricksOrdered = true;
65 
66  connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
67  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
68 
69  connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
70  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
71 
72  connect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
73  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
74  }
75 
80  ViewportBuffer::~ViewportBuffer() {
81  disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
82  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
83 
84  disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
85  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
86 
87  disconnect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
88  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
89 
90  p_dataThread = NULL;
91 
92  if(p_actions) {
93  while(!p_actions->empty()) {
94  ViewportBufferAction *action = p_actions->dequeue();
95  if(action) {
96  delete action;
97  action = NULL;
98  }
99  }
100  delete p_actions;
101  p_actions = NULL;
102  }
103 
104  emptyBuffer(true);
105  }
106 
107 
114  void ViewportBuffer::fillBuffer(QRect rect) {
115  if(p_band == -1) {
116  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
117  }
118 
119  ViewportBufferFill *newFill = createViewportBufferFill(
120  rect.intersected(bufferXYRect()), false);
121  enqueueAction(newFill);
122  doQueuedActions();
123  }
124 
125 
133  void ViewportBuffer::fillBuffer(QRect rect, const Brick *data) {
134  if(p_band == -1) {
135  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
136  }
137 
138  rect = rect.intersected(bufferXYRect());
139 
140  if (!rect.isValid())
141  return;
142 
143  try {
144  ViewportBufferFill *fill = createViewportBufferFill(rect, false);
145 
146  while(fill->shouldRequestMore()) {
147  fill->incRequestPosition();
148  fill->incReadPosition();
149 
150  for(int x = rect.left(); x <= rect.right(); x ++) {
151  // Index into internal buffer is minus leftmost/topmost pixel
152  int xIndex = x - fill->getLeftmostPixelPosition();
153  int yIndex = fill->getRequestPosition() -
154  fill->getTopmostPixelPosition();
155 
156  double samp = fill->viewportToSample(x);
157  double line = fill->viewportToLine(fill->getRequestPosition());
158  if (samp < data->Sample())
159  samp = data->Sample();
160  if (samp > data->Sample() + data->SampleDimension())
161  samp = data->Sample() + data->SampleDimension();
162  if (line < data->Line())
163  line = data->Line();
164  if (line > data->Line() + data->LineDimension())
165  line = data->Line() + data->LineDimension();
166 
167  // Index into buffer is current sample - start sample
168  // *Brick indices are in units of cube pixels, not screen pixels
169  int brickIndex = data->Index((int)(samp + 0.5), (int)(line + 0.5),
170  p_band);
171 
172  if(brickIndex < 0) {
173  p_buffer.at(yIndex).at(xIndex) = data->at(0);
174  }
175  else if(brickIndex >= data->size()) {
176  p_buffer.at(yIndex).at(xIndex) = data->at(data->size() - 1);
177  }
178  else {
179  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
180  xIndex >= (int) p_buffer.at(yIndex).size()) {
181  throw IException(IException::Programmer,
182  "index out of range",
183  _FILEINFO_);
184  }
185  else {
186  p_buffer.at(yIndex).at(xIndex) = data->at(brickIndex);
187  }
188  }
189  }
190  }
191  }
192  catch (IException &e) {
193  throw IException(e, IException::Programmer, "Failed to load brick "
194  "into buffer", _FILEINFO_);
195  }
196  }
197 
198 
208  void ViewportBuffer::DataReady(void *requester, int cubeId,
209  const Brick *brick) {
210  if(this != requester)
211  return;
212 
213  if(p_actions->empty()) {
214  throw IException(IException::Programmer, "no actions", _FILEINFO_);
215  }
216 
217  ViewportBufferAction *curAction = p_actions->head();
218 
219  if(curAction->getActionType() != ViewportBufferAction::fill ||
220  !curAction->started()) {
221  throw IException(IException::Programmer, "not a fill action", _FILEINFO_);
222  }
223 
224  ViewportBufferFill *fill = (ViewportBufferFill *) curAction;
225 
226  const QRect *rect = fill->getRect();
227 
228  int y = fill->getReadPosition(); // screen line
229 
230  // check to see if the next screen line's brick differs from this screen
231  // line's brick. If it does, which brick do we use?
232  int curBrickLine = (int) (fill->viewportToLine(y) + 0.5);
233  int nextBrickLine = (int) (fill->viewportToLine(y + 1) + 0.5);
234  bool brickOrderCorrection = p_bricksOrdered;
235  if (curBrickLine != nextBrickLine &&
236  nextBrickLine == (int) (brick->Line() + 0.5)) {
237  y++;
238  p_bricksOrdered = false;
239  }
240  else {
241  p_bricksOrdered = true;
242  }
243 
244  double samp;
245 
246  // Loop through x values of rect on screen that we want to fill
247  for(int x = rect->left(); x <= rect->right(); x++) {
248  // Index into internal buffer is minus leftmost/topmost pixel
249  int xIndex = x - fill->getLeftmostPixelPosition();
250  int yIndex = y - fill->getTopmostPixelPosition();
251 
252  samp = fill->viewportToSample(x);
253 
254  // Index into buffer is current sample - start sample
255  // *Brick indices are in units of cube pixels, not screen pixels
256  int brickIndex = (int)(samp + 0.5) - brick->Sample();
257 
258  if(brickIndex < 0) {
259  p_buffer.at(yIndex).at(xIndex) = brick->at(0);
260  }
261  else if(brickIndex >= brick->size()) {
262  p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1);
263  }
264  else {
265  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
266  xIndex >= (int) p_buffer.at(yIndex).size()) {
267  IString msg = "An index out of range error was detected. ";
268 
269  if(yIndex < 0)
270  msg += "The Y-Index [" + IString(yIndex) + "] is less than 0";
271  else if(xIndex < 0)
272  msg += "The X-Index [" + IString(xIndex) + "] is less than 0";
273  else if(yIndex > (int)p_buffer.size())
274  msg += "The Y-Index [" + IString(yIndex) + "] is greater than the "
275  "Y-Size of [" + IString((int)p_buffer.size()) + "]";
276  else if(xIndex > (int)p_buffer.at(yIndex).size())
277  msg += "The X-Index [" + IString(xIndex) + " is greater than the "
278  "X-Size of [" + IString((int) p_buffer.at(yIndex).size()) + "]";
279 
280  throw IException(IException::Programmer, msg, _FILEINFO_);
281  }
282  else {
283  p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex);
284  }
285  }
286  }
287  fill->incReadPosition();
288 
289 
290  if(fill->shouldRequestMore()) {
291  if (p_bricksOrdered) {
292  requestCubeLine(fill);
293  }
294  else {
295  if (brickOrderCorrection) {
296  requestCubeLine(fill);
297  requestCubeLine(fill);
298  }
299  }
300  }
301  else if(fill->doneReading()) {
302  delete fill;
303  fill = NULL;
304  p_actions->dequeue();
305  doQueuedActions();
306  }
307 
308  emit DoneWithData(cubeId, brick);
309  }
310 
311 
320  void ViewportBuffer::enqueueAction(ViewportBufferAction *action) {
321  if(action->getActionType() == ViewportBufferAction::fill) {
322  QRect *fillRect = ((ViewportBufferFill *)action)->getRect();
323  p_requestedFillArea += fillRect->width() * fillRect->height();
324  }
325 
326  if(p_actions->empty()) {
327  p_viewport->enableProgress();
328  }
329 
330  p_actions->enqueue(action);
331  }
332 
333 
342  const vector<double> &ViewportBuffer::getLine(int line) {
343  if(!p_bufferInitialized || !p_enabled) {
344  throw IException(IException::Programmer, "no data", _FILEINFO_);
345  }
346 
347  if(line < 0 || line >= (int)p_buffer.size()) {
348  throw IException(IException::Programmer,
349  "Invalid call to getLine",
350  _FILEINFO_);
351  }
352 
353  return p_buffer.at(line);
354  }
355 
356 
364  QRect ViewportBuffer::getXYBoundingRect() {
365  int startx, starty, endx, endy;
366  p_viewport->cubeToViewport(0.5, 0.5, startx, starty);
367 
368  // Handle case where x,y 0,0 is sample,line 0,0 (which is outside the cube)
369  // and cubeToViewport still tells us 0.5, 0.5 is at x,y 0,0.
370  double startSamp, startLine;
371  p_viewport->viewportToCube(startx, starty, startSamp, startLine);
372 
373  if(startSamp < 0.5)
374  startx ++;
375 
376  if(startLine < 0.5)
377  starty ++;
378 
379  double rightmost = p_viewport->cubeSamples() + 0.5;
380  double bottommost = p_viewport->cubeLines() + 0.5;
381 
382  p_viewport->cubeToViewport(rightmost, bottommost, endx, endy);
383 
384  if(endx < 0 || endy < 0)
385  return QRect();
386 
387  double endSamp = -1, endLine = -1;
388  p_viewport->viewportToCube(endx, endy, endSamp, endLine);
389 
390  if(endSamp > rightmost)
391  endx --;
392 
393  if(endLine > bottommost)
394  endy --;
395 
396  // Make sure our rect makes sense
397  if(startx < 0) {
398  startx = 0;
399  }
400 
401  if(starty < 0) {
402  starty = 0;
403  }
404 
405  if(endx >= p_viewport->viewport()->width()) {
406  endx = p_viewport->viewport()->width() - 1;
407  }
408 
409  if(endy >= p_viewport->viewport()->height()) {
410  endy = p_viewport->viewport()->height() - 1;
411  }
412 
413  return QRect(startx, starty, endx - startx + 1, endy - starty + 1);
414  }
415 
416 
423  bool ViewportBuffer::hasEntireCube() {
424  double sampTolerance = 0.05 * p_viewport->cubeSamples();
425  double lineTolerance = 0.05 * p_viewport->cubeLines();
426 
427  bool hasCube = true;
428 
429  hasCube &= !working();
430  hasCube &= p_sampLineBoundingRect[rectLeft] <= (1 + sampTolerance);
431  hasCube &= p_sampLineBoundingRect[rectTop] <= (1 + lineTolerance);
432  hasCube &= p_sampLineBoundingRect[rectRight] >= (p_viewport->cubeSamples() -
433  sampTolerance);
434  hasCube &= p_sampLineBoundingRect[rectBottom] >= (p_viewport->cubeLines() -
435  lineTolerance);
436  return hasCube;
437  }
438 
439 
447  QList<double> ViewportBuffer::getSampLineBoundingRect() {
448  QRect xyRect = getXYBoundingRect();
449  double ssamp, esamp, sline, eline;
450  p_viewport->viewportToCube(xyRect.left(), xyRect.top(), ssamp, sline);
451  p_viewport->viewportToCube(xyRect.right(), xyRect.bottom(), esamp, eline);
452 
453  QList<double> boundingRect;
454 
455  boundingRect.insert(rectLeft, ssamp);
456  boundingRect.insert(rectTop, sline);
457  boundingRect.insert(rectRight, esamp);
458  boundingRect.insert(rectBottom, eline);
459 
460  return boundingRect;
461  }
462 
463 
468  void ViewportBuffer::updateBoundingRects() {
469  p_oldXYBoundingRect = p_XYBoundingRect;
470  p_XYBoundingRect = getXYBoundingRect();
471 
472  p_oldSampLineBoundingRect = p_sampLineBoundingRect;
473  p_sampLineBoundingRect = getSampLineBoundingRect();
474 
475  p_oldViewportHeight = p_viewportHeight;
476  p_viewportHeight = p_viewport->viewport()->height();
477 
478  p_oldVertScrollBarPos = p_vertScrollBarPos;
479  // Add +1 to remove the black line at the top
480  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value() + 1;
481  }
482 
483 
496  ViewportBufferFill *ViewportBuffer::createViewportBufferFill(
497  QRect someRect, bool useOldY) {
498  QScrollBar *hsb = p_viewport->horizontalScrollBar();
499  int xConstCoef = hsb->value();
500  xConstCoef -= p_viewport->viewport()->width() / 2;
501 
502  // If panning over a full screen, it will try to create a fill rect that
503  // isn't actually valid. So, in the case of any bad fill rects we will
504  // fill everything.
505  if(!someRect.isValid()) {
506  throw IException(IException::Programmer, "Fill rect invalid", _FILEINFO_);
507  }
508 
509  double xScale = p_viewport->scale();
510 
511  int yConstCoef = 0;
512 
513  if(!useOldY)
514  yConstCoef = (p_vertScrollBarPos) - p_viewportHeight / 2 - 1;
515  else
516  yConstCoef = (p_oldVertScrollBarPos) - p_oldViewportHeight / 2 - 1;
517 
518  double yScale = xScale;
519 
520  QPoint topLeft;
521 
522  if(!useOldY) {
523  topLeft = p_XYBoundingRect.topLeft();
524  }
525  else {
526  topLeft = QPoint(p_XYBoundingRect.left(), p_oldXYBoundingRect.top());
527  }
528 
529  ViewportBufferFill *newFill = new ViewportBufferFill(someRect, xConstCoef,
530  xScale, yConstCoef, yScale, topLeft);
531 
532  return newFill;
533  }
534 
535 
541  void ViewportBuffer::requestCubeLine(ViewportBufferFill *fill) {
542  if(p_band == -1) {
543  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
544  }
545 
546  // Prep to create minimal buffer(s) to read the cube
547  QRect &rect = *fill->getRect();
548 
549  double ssamp = fill->viewportToSample(rect.left());
550 
551  double esamp = fill->viewportToSample(rect.right());
552 
553  int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1;
554 
555  if(brickWidth <= 0)
556  return;
557 
558  double line = fill->viewportToLine(fill->getRequestPosition());
559  int roundedSamp = (int)(ssamp + 0.5);
560  int roundedLine = (int)(line + 0.5);
561 
562  emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth,
563  roundedLine, p_band, this);
564 
565  fill->incRequestPosition();
566  }
567 
568 
579  void ViewportBuffer::doQueuedActions() {
580  bool doNextAction = false;
581 
582  ViewportBufferAction *curAction = NULL;
583 
584  // if we aren't preserving data, and we don't still need the initial
585  // stretch (on startup), let's reset the buffer.
586  if(!reinitializeActionExists() && !actionsPreserveData() &&
587  p_initialStretchDone) {
588  // Actions don't preserve data - call reinitialize!
589  reinitialize();
590  }
591 
592  if(!working()) {
593  p_requestedFillArea = 0.0;
594  }
595 
596  if(!p_actions->empty()) {
597  curAction = p_actions->head();
598  doNextAction = !curAction->started();
599  }
600 
601  while(doNextAction) {
602  if(curAction->getActionType() == ViewportBufferAction::transform) {
603  doTransformAction((ViewportBufferTransform *) curAction);
604  }
605  else if(curAction->getActionType() == ViewportBufferAction::fill) {
606  startFillAction((ViewportBufferFill *) curAction);
607  }
608  else {
609  doStretchAction((ViewportBufferStretch *) curAction);
610  p_initialStretchDone = true;
611  }
612 
613  doNextAction = !p_actions->empty();
614 
615  if(doNextAction) {
616  curAction = p_actions->head();
617  doNextAction = !curAction->started();
618  }
619  }
620 
621  if(p_actions->empty()) {
622  // Buffer Updated - Giving it BufferXYRect
623  p_viewport->bufferUpdated(bufferXYRect());
624  }
625  }
626 
627 
633  double ViewportBuffer::currentProgress() {
634  if(!working())
635  return 1.0;
636  if(p_requestedFillArea <= 0.0)
637  return 0.0;
638 
639  return 1.0 - totalUnfilledArea() / p_requestedFillArea;
640  }
641 
642 
649  double ViewportBuffer::totalUnfilledArea() {
650  double totalFillArea = 0.0;
651 
652  // If at any time the total X or Y shift exceeds the buffer size, we aren't
653  // preserving data. Check to see if this is the case!
654  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
655  ViewportBufferAction *action = (*p_actions)[actionIndex];
656 
657  if(action->getActionType() == ViewportBufferAction::fill) {
658  ViewportBufferFill *fill = (ViewportBufferFill *)action;
659 
660  QRect unfilledRect(*fill->getRect());
661  unfilledRect.setTop(fill->getReadPosition());
662  totalFillArea += unfilledRect.width() * unfilledRect.height();
663  }
664  }
665 
666  return totalFillArea;
667  }
668 
669 
674  bool ViewportBuffer::actionsPreserveData() {
675  int totalXShift = 0;
676  int totalYShift = 0;
677 
678  QRect currentBufferRect(bufferXYRect());
679 
680  int bufferWidth = currentBufferRect.width();
681  int bufferHeight = currentBufferRect.height();
682 
683  // If at any time the total X or Y shift exceeds the buffer size, we aren't
684  // preserving data. Check to see if this is the case!
685  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
686  ViewportBufferAction *action = (*p_actions)[actionIndex];
687 
688  if(action->getActionType() == ViewportBufferAction::transform) {
689  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
690 
691  if(transform->resizeFirst()) {
692  bufferWidth = transform->getBufferWidth();
693  bufferHeight = transform->getBufferHeight();
694  }
695 
696  if(abs(totalXShift) >= bufferWidth)
697  return false;
698  if(abs(totalYShift) >= bufferHeight)
699  return false;
700 
701  // Without the absolute value this will calculate
702  // if any data on the screen is preserved, however
703  // a better method is to see if its quicker to reread
704  // it all which happens when we use abs
705  totalXShift += abs(transform->getXTranslation());
706  totalYShift += abs(transform->getYTranslation());
707 
708  if(!transform->resizeFirst()) {
709  bufferWidth = transform->getBufferWidth();
710  bufferHeight = transform->getBufferHeight();
711  }
712 
713  if(abs(totalXShift) >= bufferWidth)
714  return false;
715  if(abs(totalYShift) >= bufferHeight)
716  return false;
717  }
718  }
719 
720  return true;
721  }
722 
723 
730  bool ViewportBuffer::reinitializeActionExists() {
731  QRect currentBufferRect(bufferXYRect());
732 
733  if(currentBufferRect.width() == 0 || currentBufferRect.height() == 0) {
734  return true;
735  }
736 
737  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
738  ViewportBufferAction *action = (*p_actions)[actionIndex];
739 
740  if(action->getActionType() == ViewportBufferAction::transform) {
741  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
742 
743  if(transform->getBufferWidth() == 0)
744  return true;
745  if(transform->getBufferHeight() == 0)
746  return true;
747  }
748  }
749 
750  return false;
751  }
752 
753 
759  bool ViewportBuffer::working() {
760  return !p_actions->empty() || !p_bufferInitialized || !p_enabled;
761  }
762 
763 
769  void ViewportBuffer::doTransformAction(ViewportBufferTransform *action) {
770  bool needResize = action->getBufferWidth() > -1 &&
771  action->getBufferHeight() > -1;
772 
773  if(action->resizeFirst() && needResize) {
774  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
775  }
776 
777  shiftBuffer(action->getXTranslation(), action->getYTranslation());
778 
779  if(!action->resizeFirst() && needResize) {
780  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
781  }
782 
783  delete action;
784  action = NULL;
785  p_actions->dequeue();
786  }
787 
788 
795  void ViewportBuffer::startFillAction(ViewportBufferFill *action) {
796  if(action->started())
797  return;
798 
799  action->started(true);
800 
801  requestCubeLine(action);
802 
803  if(action->shouldRequestMore()) {
804  requestCubeLine(action);
805  }
806  }
807 
808 
814  void ViewportBuffer::doStretchAction(ViewportBufferStretch *action) {
815  delete action;
816  action = NULL;
817  p_actions->dequeue();
818 
819  p_viewport->restretch(this);
820  }
821 
822 
829  void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
830  p_buffer.resize(height);
831 
832  for(unsigned int i = 0; i < p_buffer.size(); i++) {
833  p_buffer[i].resize(width, Null);
834  }
835  }
836 
837 
845  void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
846  if(deltaY >= 0) {
847  for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
848  p_buffer[i] = p_buffer[i - deltaY];
849 
850  // If we have y shift, null out original data (keep only moved data)
851  if(deltaY != 0) {
852  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
853  p_buffer[i - deltaY][x] = Null;
854  }
855  }
856 
857  if(deltaX > 0) {
858  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
859  p_buffer[i][j] = p_buffer[i][j - deltaX];
860  p_buffer[i][j - deltaX] = Null;
861  }
862  }
863  else if(deltaX < 0) {
864  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
865  p_buffer[i][j] = p_buffer[i][j - deltaX];
866  p_buffer[i][j - deltaX] = Null;
867  }
868  }
869  }
870  }
871  else if(deltaY < 0) {
872  for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
873  p_buffer[i] = p_buffer[i - deltaY];
874 
875  // null out original data (keep only moved data)
876  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
877  p_buffer[i - deltaY][x] = Null;
878  }
879 
880  if(deltaX > 0) {
881  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
882  p_buffer[i][j] = p_buffer[i][j - deltaX];
883  p_buffer[i][j - deltaX] = Null;
884  }
885  }
886  else if(deltaX < 0) {
887  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
888  p_buffer[i][j] = p_buffer[i][j - deltaX];
889  p_buffer[i][j - deltaX] = Null;
890  }
891  }
892  }
893  }
894  }
895 
896 
901  void ViewportBuffer::resizedViewport() {
902  updateBoundingRects();
903 
904  if(!p_bufferInitialized || !p_enabled)
905  return;
906 
907  // ensure we have a valid bounding rect! For example, If the cube viewport
908  // is hidden and then shown again this could happen.
909  if(!p_XYBoundingRect.isValid())
910  return;
911 
912  if(!p_oldXYBoundingRect.isValid()) {
913  reinitialize();
914  return;
915  }
916 
917  //We need to know how much data was gained/lost on each side of the cube
918  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
919  p_oldSampLineBoundingRect[rectLeft];
920  //The input to round should be close to an integer
921  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
922 
923  double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
924  p_oldSampLineBoundingRect[rectRight];
925  int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
926 
927  double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
928  p_oldSampLineBoundingRect[rectTop];
929  int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
930 
931  double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
932  p_oldSampLineBoundingRect[rectBottom];
933  int deltaBottomPixels = (int)round(deltaBottomSamples *
934  p_viewport->scale());
935 
936  //deltaW is the change in width in the visible area of the cube
937  int deltaW = - deltaLeftPixels + deltaRightPixels;
938 
939  //deltaH is the change in height in the visible area of the cube
940  int deltaH = - deltaTopPixels + deltaBottomPixels;
941 
942  //If the new visible width has changed (resized in the horizontal direction)
943  if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
944  //Resized larger in the horizontal direction
945  if(deltaW > 0) {
946  //Using old height because we might lose data if new height is smaller
948  transform->setTranslation(-deltaLeftPixels, 0);
949  transform->setResize(p_XYBoundingRect.width(),
950  p_oldXYBoundingRect.height());
951  transform->resizeFirst(true);
952 
953  enqueueAction(transform);
954 
955  // left side that needs filled
956  QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
957  p_oldXYBoundingRect.top());
958 
959  QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
960  p_oldXYBoundingRect.bottom());
961 
962  QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
963 
964  ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
965  true);
966  enqueueAction(leftFill);
967 
968  // right side that needs filled
969  QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
970  p_oldXYBoundingRect.top());
971 
972  QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
973  p_oldXYBoundingRect.bottom());
974 
975  QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
976 
977  ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
978  true);
979  enqueueAction(rightFill);
980  }
981  //Resized smaller in the horizontal direction
982  else if(deltaW < 0) {
984  transform->setTranslation(-deltaLeftPixels, 0);
985  transform->setResize(p_XYBoundingRect.width(),
986  p_oldXYBoundingRect.height());
987  transform->resizeFirst(false);
988  enqueueAction(transform);
989  }
990  }
991 
992  //If the new visible height has changed (resized in the vertical direction)
993  if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
994  //Resized larger in the vertical direction
995  if(deltaH > 0) {
997  transform->setTranslation(0, -deltaTopPixels);
998  transform->setResize(p_XYBoundingRect.width(),
999  p_XYBoundingRect.height());
1000  transform->resizeFirst(true);
1001 
1002  enqueueAction(transform);
1003 
1004  QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
1005  p_XYBoundingRect.top() - deltaTopPixels);
1006 
1007  QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
1008 
1009 
1010  QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
1011  p_XYBoundingRect.bottom() -
1012  deltaBottomPixels);
1013 
1014  QRect bottomSideToFill(topLeftOfbottomSide,
1015  p_XYBoundingRect.bottomRight());
1016 
1017  ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
1018  false);
1019  enqueueAction(topFill);
1020 
1021  ViewportBufferFill *bottomFill =
1022  createViewportBufferFill(bottomSideToFill, false);
1023  enqueueAction(bottomFill);
1024  }
1025  //Resized smaller in the vertical direction
1026  else if(deltaH < 0) {
1028 
1029  transform->setTranslation(0, -deltaTopPixels);
1030  transform->setResize(p_XYBoundingRect.width(),
1031  p_oldXYBoundingRect.height());
1032 
1033  enqueueAction(transform);
1034  }
1035  }
1036 
1037  doQueuedActions();
1038  }
1039 
1040 
1048  void ViewportBuffer::pan(int deltaX, int deltaY) {
1049  updateBoundingRects();
1050 
1051  if(!p_bufferInitialized || !p_enabled) {
1052  return;
1053  }
1054 
1055 
1056  if(p_sampLineBoundingRect == p_oldSampLineBoundingRect) {
1057  //The sample line bounding rect contains the bounds of the
1058  //screen pixels to the cube sample/line bounds. If the cube
1059  //bounds do not change, then we do not need to do anything to
1060  //the buffer.
1061  return;
1062  }
1063 
1064  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
1065  p_oldSampLineBoundingRect[rectLeft];
1066  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
1067 
1068  double deltaTopLines = p_sampLineBoundingRect[rectTop] -
1069  p_oldSampLineBoundingRect[rectTop];
1070  int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
1071 
1072  // Don't try to figure out panning beyond a full screen,
1073  // even though data could very well be preserved.
1074  if(abs(deltaY) >= p_XYBoundingRect.height() ||
1075  abs(deltaX) >= p_XYBoundingRect.width()) {
1076  reinitialize();
1077  return;
1078  }
1079 
1080  //Left side of the visible area changed (start sample is different)
1081  if(p_sampLineBoundingRect[rectLeft] != p_oldSampLineBoundingRect[rectLeft]) {
1082  //Shifting data to the right
1083  if(deltaX > 0) {
1084  // The buffer is getting bigger
1086  transform->setResize(p_XYBoundingRect.width(),
1087  p_oldXYBoundingRect.height());
1088  transform->setTranslation(-deltaLeftPixels, 0);
1089  transform->resizeFirst(true);
1090 
1091  enqueueAction(transform);
1092 
1093  QPoint topLeftOfRefill(p_XYBoundingRect.left(),
1094  p_oldXYBoundingRect.top());
1095 
1096  QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
1097  p_oldXYBoundingRect.bottom());
1098  QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
1099 
1100  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1101  enqueueAction(fill);
1102  }
1103  //Shifting data to the left
1104  else if(deltaX < 0) {
1105  //The buffer is getting smaller - no new data
1107  transform->setTranslation(-deltaLeftPixels, 0);
1108  transform->setResize(p_XYBoundingRect.width(),
1109  p_oldXYBoundingRect.height());
1110  transform->resizeFirst(false);
1111  enqueueAction(transform);
1112 
1113  // if new samples on the screen at all, mark it for reading
1114  if(p_sampLineBoundingRect[rectRight] !=
1115  p_oldSampLineBoundingRect[rectRight]) {
1116  QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
1117  p_oldXYBoundingRect.top());
1118  QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
1119  p_oldXYBoundingRect.bottom());
1120 
1121  QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
1122 
1123  ViewportBufferFill *fill = createViewportBufferFill(refillArea,
1124  true);
1125  enqueueAction(fill);
1126  }
1127  }
1128  }
1129  // Left side of the visible area is the same (start sample has not changed,
1130  // but end sample may be different)
1131  else {
1133  transform->setResize(p_XYBoundingRect.width(),
1134  p_oldXYBoundingRect.height());
1135  enqueueAction(transform);
1136 
1137  if(deltaX < 0) {
1138  QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
1139  p_oldXYBoundingRect.top());
1140 
1141  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1142  p_oldXYBoundingRect.bottom());
1143 
1144  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1145  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1146 
1147  enqueueAction(fill);
1148  }
1149  }
1150 
1151  //Top side of the visible area changed (start line is different)
1152  if(p_sampLineBoundingRect[rectTop] != p_oldSampLineBoundingRect[rectTop]) {
1153  //Shifting data down
1154  if(deltaY > 0) {
1156  transform->setTranslation(0, -deltaTopPixels);
1157  transform->setResize(p_XYBoundingRect.width(),
1158  p_XYBoundingRect.height());
1159  transform->resizeFirst(true);
1160  enqueueAction(transform);
1161 
1162  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1163  p_XYBoundingRect.top());
1164  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1165  p_XYBoundingRect.top() + deltaY);
1166  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1167  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1168  enqueueAction(fill);
1169  }
1170  //Shifting data up
1171  else if(deltaY < 0) {
1173  transform->setTranslation(0, -deltaTopPixels);
1174  transform->setResize(p_XYBoundingRect.width(),
1175  p_XYBoundingRect.height());
1176  transform->resizeFirst(false);
1177  enqueueAction(transform);
1178 
1179  // if new lines on the screen at all, mark it for reading
1180  if(p_sampLineBoundingRect[rectBottom] !=
1181  p_oldSampLineBoundingRect[rectBottom]) {
1182  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1183  p_oldXYBoundingRect.bottom() + deltaY);
1184  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1185  p_XYBoundingRect.bottom());
1186  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1187  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1188  enqueueAction(fill);
1189  }
1190  }
1191  }
1192  // Top side of the visible area is the same (start line has not changed, but
1193  // end line may be different)
1194  else {
1196  transform->setResize(p_XYBoundingRect.width(),
1197  p_XYBoundingRect.height());
1198  enqueueAction(transform);
1199 
1200  if(deltaY < 0) {
1201  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1202  p_XYBoundingRect.bottom() + deltaY);
1203  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1204  p_XYBoundingRect.bottom());
1205  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1206  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1207  enqueueAction(fill);
1208  }
1209  }
1210 
1211  doQueuedActions();
1212  }
1213 
1214 
1220  void ViewportBuffer::addStretchAction() {
1221  for(int i = 0; i < p_actions->size(); i++) {
1222  if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
1223  p_actions->removeAt(i);
1224  i --;
1225  }
1226  }
1227 
1228  enqueueAction(new ViewportBufferStretch());
1229  doQueuedActions();
1230  }
1231 
1241  void ViewportBuffer::emptyBuffer(bool force) {
1242  if(force) {
1243  p_buffer.clear();
1244  p_bufferInitialized = false;
1245  }
1246  }
1247 
1248 
1257  QRect ViewportBuffer::bufferXYRect() {
1258  QRect rect = p_XYBoundingRect;
1259 
1260  if(!rect.height() || !p_buffer.size())
1261  return QRect();
1262 
1263  if(rect.height() > (int) p_buffer.size())
1264  rect.setBottom(rect.top() + p_buffer.size() - 1);
1265 
1266  if(rect.width() > (int) p_buffer[0].size())
1267  rect.setRight(rect.left() + p_buffer[0].size() - 1);
1268 
1269  return rect;
1270  }
1271 
1272 
1281  void ViewportBuffer::scaleChanged() {
1282  if(!p_enabled)
1283  return;
1284 
1285  try {
1286  updateBoundingRects();
1287  reinitialize();
1288  }
1289  catch (IException &e) {
1290  throw IException(
1291  e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
1292  }
1293  }
1294 
1295 
1302  void ViewportBuffer::enable(bool enabled) {
1303  bool wasEnabled = p_enabled;
1304 
1305  p_enabled = enabled;
1306 
1307  if(!wasEnabled && p_enabled) {
1308  updateBoundingRects();
1309  reinitialize();
1310  }
1311  }
1312 
1313 
1319  void ViewportBuffer::setBand(int band) {
1320  if(p_band == band)
1321  return;
1322  p_band = band;
1323 
1324  updateBoundingRects();
1325 
1326  if(!p_enabled)
1327  return;
1328 
1329  reinitialize();
1330  }
1331 
1332 
1340  void ViewportBuffer::reinitialize() {
1341 
1342  try {
1343  // If we're in the middle of a process, we got an okay stretch on startup,
1344  // then we can stop what we're doing.
1345  if(working() && p_initialStretchDone) {
1346  // We only need to handle the current action, can ignore others
1347  ViewportBufferAction *curAction = p_actions->head();
1348 
1349  // Delete older actions
1350  for(int i = p_actions->size() - 1; i > 0; i--) {
1351  delete(*p_actions)[i];
1352  p_actions->pop_back();
1353  }
1354 
1355  // Deal with current action
1356  if(curAction->started()) {
1357  if(curAction->getActionType() == ViewportBufferAction::fill) {
1358  ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
1359 
1360  fill->stop();
1361 
1362  p_requestedFillArea = fill->getRect()->height() *
1363  fill->getRect()->width();
1364  }
1365  }
1366  else {
1367  delete curAction;
1368  p_actions->clear();
1369  p_requestedFillArea = 0.0;
1370  }
1371  }
1372 
1373 
1374  p_bufferInitialized = true;
1375 
1377  reset->setResize(0, 0);
1378  enqueueAction(reset);
1379 
1380  if (p_XYBoundingRect.isValid()) {
1382  transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
1383  enqueueAction(transform);
1384  ViewportBufferFill *fill = createViewportBufferFill(p_XYBoundingRect,
1385  false);
1386  enqueueAction(fill);
1387  }
1388 
1389  doQueuedActions();
1390  }
1391  catch (IException &e) {
1392  throw IException(IException::Programmer,
1393  "Unable to resize and fill buffer.",
1394  _FILEINFO_);
1395  }
1396  }
1397 }
Isis::Buffer::SampleDimension
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:70
Isis::ViewportBufferFill::doneReading
bool doneReading()
Returns true if read position is past the end of the fill.
Definition: ViewportBufferFill.cpp:87
QList< double >
Isis::ViewportBufferTransform::resizeFirst
bool resizeFirst()
Returns true if the resize should happen before the translation.
Definition: ViewportBufferTransform.h:77
Isis::Buffer::Index
int Index(const int i_samp, const int i_line, const int i_band) const
Given a sample, line, and band position, this returns the appropriate index in the shape buffer.
Definition: Buffer.cpp:197
Isis::ViewportBufferFill
Definition: ViewportBufferFill.h:29
Isis::ViewportBufferTransform::getYTranslation
int getYTranslation()
Gets the amount the buffer should be translated in Y.
Definition: ViewportBufferTransform.h:47
Isis::ViewportBufferStretch
Definition: ViewportBufferStretch.h:22
Isis::Brick
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:45
Isis::CubeDataThread
Encapsulation of Cube I/O with Change Notifications.
Definition: CubeDataThread.h:59
Isis::ViewportBufferTransform
Definition: ViewportBufferTransform.h:21
Isis::ViewportBufferFill::viewportToSample
double viewportToSample(int x)
Converts screen x position to cube sample position.
Definition: ViewportBufferFill.h:53
Isis::ViewportBufferFill::viewportToLine
double viewportToLine(int y)
Converts screen y position to cube line position.
Definition: ViewportBufferFill.h:64
Isis::ViewportBufferTransform::getBufferWidth
const int & getBufferWidth()
Returns the new buffer width.
Definition: ViewportBufferTransform.h:58
Isis::ViewportBufferFill::getReadPosition
int getReadPosition() const
Returns the current read position.
Definition: ViewportBufferFill.h:82
Isis::ViewportBufferFill::getTopmostPixelPosition
int getTopmostPixelPosition()
Returns the top of the X/Y bounding rect for this fill.
Definition: ViewportBufferFill.cpp:67
Isis::ViewportBufferAction
Definition: ViewportBufferAction.h:18
Isis::ViewportBufferFill::shouldRequestMore
bool shouldRequestMore()
Returns true if request position is past the end of the fill.
Definition: ViewportBufferFill.cpp:97
Isis::ViewportBufferFill::getRect
QRect * getRect()
Returns the rect that this action is filling in screen pixels.
Definition: ViewportBufferFill.h:101
Isis::Buffer::LineDimension
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:79
Isis::Buffer::at
double at(const int index) const
Returns the value in the shape buffer at the given index.
Definition: Buffer.cpp:231
Isis::ViewportBufferTransform::setTranslation
void setTranslation(int x, int y)
Sets the translation amount in x and y.
Definition: ViewportBufferTransform.cpp:32
Isis::ViewportBufferFill::getLeftmostPixelPosition
int getLeftmostPixelPosition()
Returns the left of the X/Y bounding rect for this fill.
Definition: ViewportBufferFill.cpp:77
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::CubeViewport
Widget to display Isis cubes for qt apps.
Definition: CubeViewport.h:122
Isis::Null
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:95
Isis::ViewportBufferFill::incRequestPosition
void incRequestPosition()
Increment request position.
Definition: ViewportBufferFill.h:92
Isis::ViewportBufferFill::getRequestPosition
int getRequestPosition() const
Returns the current request position (>= read position)
Definition: ViewportBufferFill.h:73
Isis::ViewportBufferAction::started
bool started()
Returns true if this is an action that takes time and has begun.
Definition: ViewportBufferAction.h:39
std
Namespace for the standard library.
QQueue
This is free and unencumbered software released into the public domain.
Definition: BoxcarCachingAlgorithm.h:14
Isis::ViewportBufferTransform::setResize
void setResize(int width, int height)
Sets the size the buffer should be resized to.
Definition: ViewportBufferTransform.cpp:44
Isis::ViewportBufferAction::getActionType
virtual ActionType getActionType()
Returns the instantiated type.
Definition: ViewportBufferAction.h:34
Isis::ViewportBufferFill::stop
void stop()
Cancels the current operation.
Definition: ViewportBufferFill.cpp:105
Isis::Buffer::size
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:97
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::ViewportBufferFill::incReadPosition
void incReadPosition()
Increment read position.
Definition: ViewportBufferFill.h:87
Isis::Buffer::Sample
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:127
Isis::ViewportBufferTransform::getBufferHeight
const int & getBufferHeight()
Returns the new buffer height.
Definition: ViewportBufferTransform.h:67
Isis::Buffer::Line
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:145
Isis::ViewportBufferTransform::getXTranslation
int getXTranslation()
Gets the amount the buffer should be translated in X.
Definition: ViewportBufferTransform.h:37
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the USGS Astrogeology Discussion Board
To report a bug, or suggest a feature go to: ISIS Github
File Modified: 07/13/2023 15:17:26