USGS

Isis 3.0 Object Programmers' Reference

Home

ViewportBuffer.cpp
1 #include "IsisDebug.h"
2 
3 #include "ViewportBuffer.h"
4 #include "ViewportBufferAction.h"
5 #include "ViewportBufferStretch.h"
6 #include "ViewportBufferFill.h"
7 #include "ViewportBufferTransform.h"
8 
9 #include <QApplication>
10 #include <QQueue>
11 #include <QRect>
12 #include <QScrollBar>
13 
14 #include "Brick.h"
15 #include "CubeDataThread.h"
16 #include "CubeViewport.h"
17 #include "SpecialPixel.h"
18 #include "PixelType.h"
19 
20 
21 #define round(x) ((x) > 0.0 ? (x) + 0.5 : (x) - 0.5)
22 
23 
24 using namespace std;
25 
26 
27 namespace Isis {
36  ViewportBuffer::ViewportBuffer(CubeViewport *viewport,
37  CubeDataThread *cubeData,
38  int cubeId) {
39  p_dataThread = cubeData;
40  p_cubeId = cubeId;
41 
42  p_actions = 0;
43 
44  p_actions = new QQueue< ViewportBufferAction * >();
45  p_viewport = viewport;
46  p_bufferInitialized = false;
47  p_band = -1;
48  p_enabled = true;
49  p_initialStretchDone = false;
50  p_viewportHeight = p_viewport->viewport()->height();
51  p_oldViewportHeight = p_viewport->viewport()->height();
52  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value();
53  p_oldVertScrollBarPos = p_viewport->verticalScrollBar()->value();
54 
55  p_requestedFillArea = 0.0;
56  p_bricksOrdered = true;
57 
58  connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
59  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
60 
61  connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
62  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
63 
64  connect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
65  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
66  }
67 
72  ViewportBuffer::~ViewportBuffer() {
73  disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
74  p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
75 
76  disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
77  this, SLOT(DataReady(void *, int, const Isis::Brick *)));
78 
79  disconnect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
80  p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
81 
82  p_dataThread = NULL;
83 
84  if(p_actions) {
85  while(!p_actions->empty()) {
86  ViewportBufferAction *action = p_actions->dequeue();
87  if(action) {
88  delete action;
89  action = NULL;
90  }
91  }
92  delete p_actions;
93  p_actions = NULL;
94  }
95 
96  emptyBuffer(true);
97  }
98 
99 
106  void ViewportBuffer::fillBuffer(QRect rect) {
107  if(p_band == -1) {
108  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
109  }
110 
111  ViewportBufferFill *newFill = createViewportBufferFill(
112  rect.intersected(bufferXYRect()), false);
113  enqueueAction(newFill);
114  doQueuedActions();
115  }
116 
117 
125  void ViewportBuffer::fillBuffer(QRect rect, const Brick *data) {
126  if(p_band == -1) {
127  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
128  }
129 
130  rect = rect.intersected(bufferXYRect());
131 
132  if (!rect.isValid())
133  return;
134 
135  try {
136  ViewportBufferFill *fill = createViewportBufferFill(rect, false);
137 
138  while(fill->shouldRequestMore()) {
139  fill->incRequestPosition();
140  fill->incReadPosition();
141 
142  for(int x = rect.left(); x <= rect.right(); x ++) {
143  // Index into internal buffer is minus leftmost/topmost pixel
144  int xIndex = x - fill->getLeftmostPixelPosition();
145  int yIndex = fill->getRequestPosition() -
146  fill->getTopmostPixelPosition();
147 
148  double samp = fill->viewportToSample(x);
149  double line = fill->viewportToLine(fill->getRequestPosition());
150  if (samp < data->Sample())
151  samp = data->Sample();
152  if (samp > data->Sample() + data->SampleDimension())
153  samp = data->Sample() + data->SampleDimension();
154  if (line < data->Line())
155  line = data->Line();
156  if (line > data->Line() + data->LineDimension())
157  line = data->Line() + data->LineDimension();
158 
159  // Index into buffer is current sample - start sample
160  // *Brick indices are in units of cube pixels, not screen pixels
161  int brickIndex = data->Index((int)(samp + 0.5), (int)(line + 0.5),
162  p_band);
163 
164  if(brickIndex < 0) {
165  p_buffer.at(yIndex).at(xIndex) = data->at(0);
166  }
167  else if(brickIndex >= data->size()) {
168  p_buffer.at(yIndex).at(xIndex) = data->at(data->size() - 1);
169  }
170  else {
171  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
172  xIndex >= (int) p_buffer.at(yIndex).size()) {
173  throw IException(IException::Programmer,
174  "index out of range",
175  _FILEINFO_);
176  }
177  else {
178  p_buffer.at(yIndex).at(xIndex) = data->at(brickIndex);
179  }
180  }
181  }
182  }
183  }
184  catch (IException &e) {
185  throw IException(e, IException::Programmer, "Failed to load brick "
186  "into buffer", _FILEINFO_);
187  }
188  }
189 
190 
200  void ViewportBuffer::DataReady(void *requester, int cubeId,
201  const Brick *brick) {
202  if(this != requester)
203  return;
204 
205  if(p_actions->empty()) {
206  throw IException(IException::Programmer, "no actions", _FILEINFO_);
207  }
208 
209  ViewportBufferAction *curAction = p_actions->head();
210 
211  if(curAction->getActionType() != ViewportBufferAction::fill ||
212  !curAction->started()) {
213  throw IException(IException::Programmer, "not a fill action", _FILEINFO_);
214  }
215 
216  ViewportBufferFill *fill = (ViewportBufferFill *) curAction;
217 
218  const QRect *rect = fill->getRect();
219 
220  int y = fill->getReadPosition(); // screen line
221 
222  // check to see if the next screen line's brick differs from this screen
223  // line's brick. If it does, which brick do we use?
224  int curBrickLine = (int) (fill->viewportToLine(y) + 0.5);
225  int nextBrickLine = (int) (fill->viewportToLine(y + 1) + 0.5);
226  bool brickOrderCorrection = p_bricksOrdered;
227  if (curBrickLine != nextBrickLine &&
228  nextBrickLine == (int) (brick->Line() + 0.5)) {
229  y++;
230  p_bricksOrdered = false;
231  }
232  else {
233  p_bricksOrdered = true;
234  }
235 
236  double samp;
237 
238  // Loop through x values of rect on screen that we want to fill
239  for(int x = rect->left(); x <= rect->right(); x++) {
240  // Index into internal buffer is minus leftmost/topmost pixel
241  int xIndex = x - fill->getLeftmostPixelPosition();
242  int yIndex = y - fill->getTopmostPixelPosition();
243 
244  samp = fill->viewportToSample(x);
245 
246  // Index into buffer is current sample - start sample
247  // *Brick indices are in units of cube pixels, not screen pixels
248  int brickIndex = (int)(samp + 0.5) - brick->Sample();
249 
250  if(brickIndex < 0) {
251  p_buffer.at(yIndex).at(xIndex) = brick->at(0);
252  }
253  else if(brickIndex >= brick->size()) {
254  p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1);
255  }
256  else {
257  if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
258  xIndex >= (int) p_buffer.at(yIndex).size()) {
259  IString msg = "An index out of range error was detected. ";
260 
261  if(yIndex < 0)
262  msg += "The Y-Index [" + IString(yIndex) + "] is less than 0";
263  else if(xIndex < 0)
264  msg += "The X-Index [" + IString(xIndex) + "] is less than 0";
265  else if(yIndex > (int)p_buffer.size())
266  msg += "The Y-Index [" + IString(yIndex) + "] is greater than the "
267  "Y-Size of [" + IString((int)p_buffer.size()) + "]";
268  else if(xIndex > (int)p_buffer.at(yIndex).size())
269  msg += "The X-Index [" + IString(xIndex) + " is greater than the "
270  "X-Size of [" + IString((int) p_buffer.at(yIndex).size()) + "]";
271 
272  throw IException(IException::Programmer, msg, _FILEINFO_);
273  }
274  else {
275  p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex);
276  }
277  }
278  }
279  fill->incReadPosition();
280 
281 
282  if(fill->shouldRequestMore()) {
283  if (p_bricksOrdered) {
284  requestCubeLine(fill);
285  }
286  else {
287  if (brickOrderCorrection) {
288  requestCubeLine(fill);
289  requestCubeLine(fill);
290  }
291  }
292  }
293  else if(fill->doneReading()) {
294  delete fill;
295  fill = NULL;
296  p_actions->dequeue();
297  doQueuedActions();
298  }
299 
300  emit DoneWithData(cubeId, brick);
301  }
302 
303 
312  void ViewportBuffer::enqueueAction(ViewportBufferAction *action) {
313  if(action->getActionType() == ViewportBufferAction::fill) {
314  QRect *fillRect = ((ViewportBufferFill *)action)->getRect();
315  p_requestedFillArea += fillRect->width() * fillRect->height();
316  }
317 
318  if(p_actions->empty()) {
319  p_viewport->enableProgress();
320  }
321 
322  p_actions->enqueue(action);
323  }
324 
325 
334  const vector<double> &ViewportBuffer::getLine(int line) {
335  if(!p_bufferInitialized || !p_enabled) {
336  throw IException(IException::Programmer, "no data", _FILEINFO_);
337  }
338 
339  if(line < 0 || line >= (int)p_buffer.size()) {
340  throw IException(IException::Programmer,
341  "Invalid call to getLine",
342  _FILEINFO_);
343  }
344 
345  return p_buffer.at(line);
346  }
347 
348 
356  QRect ViewportBuffer::getXYBoundingRect() {
357  int startx, starty, endx, endy;
358  p_viewport->cubeToViewport(0.5, 0.5, startx, starty);
359 
360  // Handle case where x,y 0,0 is sample,line 0,0 (which is outside the cube)
361  // and cubeToViewport still tells us 0.5, 0.5 is at x,y 0,0.
362  double startSamp, startLine;
363  p_viewport->viewportToCube(startx, starty, startSamp, startLine);
364 
365  if(startSamp < 0.5)
366  startx ++;
367 
368  if(startLine < 0.5)
369  starty ++;
370 
371  double rightmost = p_viewport->cubeSamples() + 0.5;
372  double bottommost = p_viewport->cubeLines() + 0.5;
373 
374  p_viewport->cubeToViewport(rightmost, bottommost, endx, endy);
375 
376  if(endx < 0 || endy < 0)
377  return QRect();
378 
379  double endSamp = -1, endLine = -1;
380  p_viewport->viewportToCube(endx, endy, endSamp, endLine);
381 
382  if(endSamp > rightmost)
383  endx --;
384 
385  if(endLine > bottommost)
386  endy --;
387 
388  // Make sure our rect makes sense
389  if(startx < 0) {
390  startx = 0;
391  }
392 
393  if(starty < 0) {
394  starty = 0;
395  }
396 
397  if(endx >= p_viewport->viewport()->width()) {
398  endx = p_viewport->viewport()->width() - 1;
399  }
400 
401  if(endy >= p_viewport->viewport()->height()) {
402  endy = p_viewport->viewport()->height() - 1;
403  }
404 
405  return QRect(startx, starty, endx - startx + 1, endy - starty + 1);
406  }
407 
408 
415  bool ViewportBuffer::hasEntireCube() {
416  double sampTolerance = 0.05 * p_viewport->cubeSamples();
417  double lineTolerance = 0.05 * p_viewport->cubeLines();
418 
419  bool hasCube = true;
420 
421  hasCube &= !working();
422  hasCube &= p_sampLineBoundingRect[rectLeft] <= (1 + sampTolerance);
423  hasCube &= p_sampLineBoundingRect[rectTop] <= (1 + lineTolerance);
424  hasCube &= p_sampLineBoundingRect[rectRight] >= (p_viewport->cubeSamples() -
425  sampTolerance);
426  hasCube &= p_sampLineBoundingRect[rectBottom] >= (p_viewport->cubeLines() -
427  lineTolerance);
428  return hasCube;
429  }
430 
431 
439  QList<double> ViewportBuffer::getSampLineBoundingRect() {
440  QRect xyRect = getXYBoundingRect();
441  double ssamp, esamp, sline, eline;
442  p_viewport->viewportToCube(xyRect.left(), xyRect.top(), ssamp, sline);
443  p_viewport->viewportToCube(xyRect.right(), xyRect.bottom(), esamp, eline);
444 
445  QList<double> boundingRect;
446 
447  boundingRect.insert(rectLeft, ssamp);
448  boundingRect.insert(rectTop, sline);
449  boundingRect.insert(rectRight, esamp);
450  boundingRect.insert(rectBottom, eline);
451 
452  return boundingRect;
453  }
454 
455 
460  void ViewportBuffer::updateBoundingRects() {
461  p_oldXYBoundingRect = p_XYBoundingRect;
462  p_XYBoundingRect = getXYBoundingRect();
463 
464  p_oldSampLineBoundingRect = p_sampLineBoundingRect;
465  p_sampLineBoundingRect = getSampLineBoundingRect();
466 
467  p_oldViewportHeight = p_viewportHeight;
468  p_viewportHeight = p_viewport->viewport()->height();
469 
470  p_oldVertScrollBarPos = p_vertScrollBarPos;
471  // Add +1 to remove the black line at the top
472  p_vertScrollBarPos = p_viewport->verticalScrollBar()->value() + 1;
473  }
474 
475 
488  ViewportBufferFill *ViewportBuffer::createViewportBufferFill(
489  QRect someRect, bool useOldY) {
490  QScrollBar *hsb = p_viewport->horizontalScrollBar();
491  int xConstCoef = hsb->value();
492  xConstCoef -= p_viewport->viewport()->width() / 2;
493 
494  // If panning over a full screen, it will try to create a fill rect that
495  // isn't actually valid. So, in the case of any bad fill rects we will
496  // fill everything.
497  if(!someRect.isValid()) {
498  throw IException(IException::Programmer, "Fill rect invalid", _FILEINFO_);
499  }
500 
501  double xScale = p_viewport->scale();
502 
503  int yConstCoef = 0;
504 
505  if(!useOldY)
506  yConstCoef = (p_vertScrollBarPos) - p_viewportHeight / 2 - 1;
507  else
508  yConstCoef = (p_oldVertScrollBarPos) - p_oldViewportHeight / 2 - 1;
509 
510  double yScale = xScale;
511 
512  QPoint topLeft;
513 
514  if(!useOldY) {
515  topLeft = p_XYBoundingRect.topLeft();
516  }
517  else {
518  topLeft = QPoint(p_XYBoundingRect.left(), p_oldXYBoundingRect.top());
519  }
520 
521  ViewportBufferFill *newFill = new ViewportBufferFill(someRect, xConstCoef,
522  xScale, yConstCoef, yScale, topLeft);
523 
524  return newFill;
525  }
526 
527 
533  void ViewportBuffer::requestCubeLine(ViewportBufferFill *fill) {
534  if(p_band == -1) {
535  throw IException(IException::Programmer, "invalid band", _FILEINFO_);
536  }
537 
538  // Prep to create minimal buffer(s) to read the cube
539  QRect &rect = *fill->getRect();
540 
541  double ssamp = fill->viewportToSample(rect.left());
542 
543  double esamp = fill->viewportToSample(rect.right());
544 
545  int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1;
546 
547  if(brickWidth <= 0)
548  return;
549 
550  double line = fill->viewportToLine(fill->getRequestPosition());
551  int roundedSamp = (int)(ssamp + 0.5);
552  int roundedLine = (int)(line + 0.5);
553 
554  emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth,
555  roundedLine, p_band, this);
556 
557  fill->incRequestPosition();
558  }
559 
560 
571  void ViewportBuffer::doQueuedActions() {
572  bool doNextAction = false;
573 
574  ViewportBufferAction *curAction = NULL;
575 
576  // if we aren't preserving data, and we don't still need the initial
577  // stretch (on startup), let's reset the buffer.
578  if(!reinitializeActionExists() && !actionsPreserveData() &&
579  p_initialStretchDone) {
580  // Actions don't preserve data - call reinitialize!
581  reinitialize();
582  }
583 
584  if(!working()) {
585  p_requestedFillArea = 0.0;
586  }
587 
588  if(!p_actions->empty()) {
589  curAction = p_actions->head();
590  doNextAction = !curAction->started();
591  }
592 
593  while(doNextAction) {
594  if(curAction->getActionType() == ViewportBufferAction::transform) {
595  doTransformAction((ViewportBufferTransform *) curAction);
596  }
597  else if(curAction->getActionType() == ViewportBufferAction::fill) {
598  startFillAction((ViewportBufferFill *) curAction);
599  }
600  else {
601  doStretchAction((ViewportBufferStretch *) curAction);
602  p_initialStretchDone = true;
603  }
604 
605  doNextAction = !p_actions->empty();
606 
607  if(doNextAction) {
608  curAction = p_actions->head();
609  doNextAction = !curAction->started();
610  }
611  }
612 
613  if(p_actions->empty()) {
614  // Buffer Updated - Giving it BufferXYRect
615  p_viewport->bufferUpdated(bufferXYRect());
616  }
617  }
618 
619 
625  double ViewportBuffer::currentProgress() {
626  if(!working())
627  return 1.0;
628  if(p_requestedFillArea <= 0.0)
629  return 0.0;
630 
631  return 1.0 - totalUnfilledArea() / p_requestedFillArea;
632  }
633 
634 
641  double ViewportBuffer::totalUnfilledArea() {
642  double totalFillArea = 0.0;
643 
644  // If at any time the total X or Y shift exceeds the buffer size, we aren't
645  // preserving data. Check to see if this is the case!
646  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
647  ViewportBufferAction *action = (*p_actions)[actionIndex];
648 
649  if(action->getActionType() == ViewportBufferAction::fill) {
650  ViewportBufferFill *fill = (ViewportBufferFill *)action;
651 
652  QRect unfilledRect(*fill->getRect());
653  unfilledRect.setTop(fill->getReadPosition());
654  totalFillArea += unfilledRect.width() * unfilledRect.height();
655  }
656  }
657 
658  return totalFillArea;
659  }
660 
661 
666  bool ViewportBuffer::actionsPreserveData() {
667  int totalXShift = 0;
668  int totalYShift = 0;
669 
670  QRect currentBufferRect(bufferXYRect());
671 
672  int bufferWidth = currentBufferRect.width();
673  int bufferHeight = currentBufferRect.height();
674 
675  // If at any time the total X or Y shift exceeds the buffer size, we aren't
676  // preserving data. Check to see if this is the case!
677  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
678  ViewportBufferAction *action = (*p_actions)[actionIndex];
679 
680  if(action->getActionType() == ViewportBufferAction::transform) {
681  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
682 
683  if(transform->resizeFirst()) {
684  bufferWidth = transform->getBufferWidth();
685  bufferHeight = transform->getBufferHeight();
686  }
687 
688  if(abs(totalXShift) >= bufferWidth)
689  return false;
690  if(abs(totalYShift) >= bufferHeight)
691  return false;
692 
693  // Without the absolute value this will calculate
694  // if any data on the screen is preserved, however
695  // a better method is to see if its quicker to reread
696  // it all which happens when we use abs
697  totalXShift += abs(transform->getXTranslation());
698  totalYShift += abs(transform->getYTranslation());
699 
700  if(!transform->resizeFirst()) {
701  bufferWidth = transform->getBufferWidth();
702  bufferHeight = transform->getBufferHeight();
703  }
704 
705  if(abs(totalXShift) >= bufferWidth)
706  return false;
707  if(abs(totalYShift) >= bufferHeight)
708  return false;
709  }
710  }
711 
712  return true;
713  }
714 
715 
722  bool ViewportBuffer::reinitializeActionExists() {
723  QRect currentBufferRect(bufferXYRect());
724 
725  if(currentBufferRect.width() == 0 || currentBufferRect.height() == 0) {
726  return true;
727  }
728 
729  for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
730  ViewportBufferAction *action = (*p_actions)[actionIndex];
731 
732  if(action->getActionType() == ViewportBufferAction::transform) {
733  ViewportBufferTransform *transform = (ViewportBufferTransform *)action;
734 
735  if(transform->getBufferWidth() == 0)
736  return true;
737  if(transform->getBufferHeight() == 0)
738  return true;
739  }
740  }
741 
742  return false;
743  }
744 
745 
751  bool ViewportBuffer::working() {
752  return !p_actions->empty() || !p_bufferInitialized || !p_enabled;
753  }
754 
755 
761  void ViewportBuffer::doTransformAction(ViewportBufferTransform *action) {
762  bool needResize = action->getBufferWidth() > -1 &&
763  action->getBufferHeight() > -1;
764 
765  if(action->resizeFirst() && needResize) {
766  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
767  }
768 
769  shiftBuffer(action->getXTranslation(), action->getYTranslation());
770 
771  if(!action->resizeFirst() && needResize) {
772  resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
773  }
774 
775  delete action;
776  action = NULL;
777  p_actions->dequeue();
778  }
779 
780 
787  void ViewportBuffer::startFillAction(ViewportBufferFill *action) {
788  if(action->started())
789  return;
790 
791  action->started(true);
792 
793  requestCubeLine(action);
794 
795  if(action->shouldRequestMore()) {
796  requestCubeLine(action);
797  }
798  }
799 
800 
806  void ViewportBuffer::doStretchAction(ViewportBufferStretch *action) {
807  delete action;
808  action = NULL;
809  p_actions->dequeue();
810 
811  p_viewport->restretch(this);
812  }
813 
814 
821  void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
822  p_buffer.resize(height);
823 
824  for(unsigned int i = 0; i < p_buffer.size(); i++) {
825  p_buffer[i].resize(width, Null);
826  }
827  }
828 
829 
837  void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
838  if(deltaY >= 0) {
839  for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
840  p_buffer[i] = p_buffer[i - deltaY];
841 
842  // If we have y shift, null out original data (keep only moved data)
843  if(deltaY != 0) {
844  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
845  p_buffer[i - deltaY][x] = Null;
846  }
847  }
848 
849  if(deltaX > 0) {
850  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
851  p_buffer[i][j] = p_buffer[i][j - deltaX];
852  p_buffer[i][j - deltaX] = Null;
853  }
854  }
855  else if(deltaX < 0) {
856  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
857  p_buffer[i][j] = p_buffer[i][j - deltaX];
858  p_buffer[i][j - deltaX] = Null;
859  }
860  }
861  }
862  }
863  else if(deltaY < 0) {
864  for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
865  p_buffer[i] = p_buffer[i - deltaY];
866 
867  // null out original data (keep only moved data)
868  for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
869  p_buffer[i - deltaY][x] = Null;
870  }
871 
872  if(deltaX > 0) {
873  for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
874  p_buffer[i][j] = p_buffer[i][j - deltaX];
875  p_buffer[i][j - deltaX] = Null;
876  }
877  }
878  else if(deltaX < 0) {
879  for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
880  p_buffer[i][j] = p_buffer[i][j - deltaX];
881  p_buffer[i][j - deltaX] = Null;
882  }
883  }
884  }
885  }
886  }
887 
888 
893  void ViewportBuffer::resizedViewport() {
894  updateBoundingRects();
895 
896  if(!p_bufferInitialized || !p_enabled)
897  return;
898 
899  // ensure we have a valid bounding rect! For example, If the cube viewport
900  // is hidden and then shown again this could happen.
901  if(!p_XYBoundingRect.isValid())
902  return;
903 
904  if(!p_oldXYBoundingRect.isValid()) {
905  reinitialize();
906  return;
907  }
908 
909  //We need to know how much data was gained/lost on each side of the cube
910  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
911  p_oldSampLineBoundingRect[rectLeft];
912  //The input to round should be close to an integer
913  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
914 
915  double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
916  p_oldSampLineBoundingRect[rectRight];
917  int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
918 
919  double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
920  p_oldSampLineBoundingRect[rectTop];
921  int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
922 
923  double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
924  p_oldSampLineBoundingRect[rectBottom];
925  int deltaBottomPixels = (int)round(deltaBottomSamples *
926  p_viewport->scale());
927 
928  //deltaW is the change in width in the visible area of the cube
929  int deltaW = - deltaLeftPixels + deltaRightPixels;
930 
931  //deltaH is the change in height in the visible area of the cube
932  int deltaH = - deltaTopPixels + deltaBottomPixels;
933 
934  //If the new visible width has changed (resized in the horizontal direction)
935  if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
936  //Resized larger in the horizontal direction
937  if(deltaW > 0) {
938  //Using old height because we might lose data if new height is smaller
940  transform->setTranslation(-deltaLeftPixels, 0);
941  transform->setResize(p_XYBoundingRect.width(),
942  p_oldXYBoundingRect.height());
943  transform->resizeFirst(true);
944 
945  enqueueAction(transform);
946 
947  // left side that needs filled
948  QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
949  p_oldXYBoundingRect.top());
950 
951  QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
952  p_oldXYBoundingRect.bottom());
953 
954  QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
955 
956  ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
957  true);
958  enqueueAction(leftFill);
959 
960  // right side that needs filled
961  QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
962  p_oldXYBoundingRect.top());
963 
964  QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
965  p_oldXYBoundingRect.bottom());
966 
967  QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
968 
969  ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
970  true);
971  enqueueAction(rightFill);
972  }
973  //Resized smaller in the horizontal direction
974  else if(deltaW < 0) {
976  transform->setTranslation(-deltaLeftPixels, 0);
977  transform->setResize(p_XYBoundingRect.width(),
978  p_oldXYBoundingRect.height());
979  transform->resizeFirst(false);
980  enqueueAction(transform);
981  }
982  }
983 
984  //If the new visible height has changed (resized in the vertical direction)
985  if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
986  //Resized larger in the vertical direction
987  if(deltaH > 0) {
989  transform->setTranslation(0, -deltaTopPixels);
990  transform->setResize(p_XYBoundingRect.width(),
991  p_XYBoundingRect.height());
992  transform->resizeFirst(true);
993 
994  enqueueAction(transform);
995 
996  QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
997  p_XYBoundingRect.top() - deltaTopPixels);
998 
999  QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
1000 
1001 
1002  QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
1003  p_XYBoundingRect.bottom() -
1004  deltaBottomPixels);
1005 
1006  QRect bottomSideToFill(topLeftOfbottomSide,
1007  p_XYBoundingRect.bottomRight());
1008 
1009  ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
1010  false);
1011  enqueueAction(topFill);
1012 
1013  ViewportBufferFill *bottomFill =
1014  createViewportBufferFill(bottomSideToFill, false);
1015  enqueueAction(bottomFill);
1016  }
1017  //Resized smaller in the vertical direction
1018  else if(deltaH < 0) {
1020 
1021  transform->setTranslation(0, -deltaTopPixels);
1022  transform->setResize(p_XYBoundingRect.width(),
1023  p_oldXYBoundingRect.height());
1024 
1025  enqueueAction(transform);
1026  }
1027  }
1028 
1029  doQueuedActions();
1030  }
1031 
1032 
1040  void ViewportBuffer::pan(int deltaX, int deltaY) {
1041  updateBoundingRects();
1042 
1043  if(!p_bufferInitialized || !p_enabled) {
1044  return;
1045  }
1046 
1047 
1048  if(p_sampLineBoundingRect == p_oldSampLineBoundingRect) {
1049  //The sample line bounding rect contains the bounds of the
1050  //screen pixels to the cube sample/line bounds. If the cube
1051  //bounds do not change, then we do not need to do anything to
1052  //the buffer.
1053  return;
1054  }
1055 
1056  double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
1057  p_oldSampLineBoundingRect[rectLeft];
1058  int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
1059 
1060  double deltaTopLines = p_sampLineBoundingRect[rectTop] -
1061  p_oldSampLineBoundingRect[rectTop];
1062  int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
1063 
1064  // Don't try to figure out panning beyond a full screen,
1065  // even though data could very well be preserved.
1066  if(abs(deltaY) >= p_XYBoundingRect.height() ||
1067  abs(deltaX) >= p_XYBoundingRect.width()) {
1068  reinitialize();
1069  return;
1070  }
1071 
1072  //Left side of the visible area changed (start sample is different)
1073  if(p_sampLineBoundingRect[rectLeft] != p_oldSampLineBoundingRect[rectLeft]) {
1074  //Shifting data to the right
1075  if(deltaX > 0) {
1076  // The buffer is getting bigger
1078  transform->setResize(p_XYBoundingRect.width(),
1079  p_oldXYBoundingRect.height());
1080  transform->setTranslation(-deltaLeftPixels, 0);
1081  transform->resizeFirst(true);
1082 
1083  enqueueAction(transform);
1084 
1085  QPoint topLeftOfRefill(p_XYBoundingRect.left(),
1086  p_oldXYBoundingRect.top());
1087 
1088  QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
1089  p_oldXYBoundingRect.bottom());
1090  QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
1091 
1092  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1093  enqueueAction(fill);
1094  }
1095  //Shifting data to the left
1096  else if(deltaX < 0) {
1097  //The buffer is getting smaller - no new data
1099  transform->setTranslation(-deltaLeftPixels, 0);
1100  transform->setResize(p_XYBoundingRect.width(),
1101  p_oldXYBoundingRect.height());
1102  transform->resizeFirst(false);
1103  enqueueAction(transform);
1104 
1105  // if new samples on the screen at all, mark it for reading
1106  if(p_sampLineBoundingRect[rectRight] !=
1107  p_oldSampLineBoundingRect[rectRight]) {
1108  QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
1109  p_oldXYBoundingRect.top());
1110  QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
1111  p_oldXYBoundingRect.bottom());
1112 
1113  QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
1114 
1115  ViewportBufferFill *fill = createViewportBufferFill(refillArea,
1116  true);
1117  enqueueAction(fill);
1118  }
1119  }
1120  }
1121  // Left side of the visible area is the same (start sample has not changed,
1122  // but end sample may be different)
1123  else {
1125  transform->setResize(p_XYBoundingRect.width(),
1126  p_oldXYBoundingRect.height());
1127  enqueueAction(transform);
1128 
1129  if(deltaX < 0) {
1130  QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
1131  p_oldXYBoundingRect.top());
1132 
1133  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1134  p_oldXYBoundingRect.bottom());
1135 
1136  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1137  ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1138 
1139  enqueueAction(fill);
1140  }
1141  }
1142 
1143  //Top side of the visible area changed (start line is different)
1144  if(p_sampLineBoundingRect[rectTop] != p_oldSampLineBoundingRect[rectTop]) {
1145  //Shifting data down
1146  if(deltaY > 0) {
1148  transform->setTranslation(0, -deltaTopPixels);
1149  transform->setResize(p_XYBoundingRect.width(),
1150  p_XYBoundingRect.height());
1151  transform->resizeFirst(true);
1152  enqueueAction(transform);
1153 
1154  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1155  p_XYBoundingRect.top());
1156  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1157  p_XYBoundingRect.top() + deltaY);
1158  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1159  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1160  enqueueAction(fill);
1161  }
1162  //Shifting data up
1163  else if(deltaY < 0) {
1165  transform->setTranslation(0, -deltaTopPixels);
1166  transform->setResize(p_XYBoundingRect.width(),
1167  p_XYBoundingRect.height());
1168  transform->resizeFirst(false);
1169  enqueueAction(transform);
1170 
1171  // if new lines on the screen at all, mark it for reading
1172  if(p_sampLineBoundingRect[rectBottom] !=
1173  p_oldSampLineBoundingRect[rectBottom]) {
1174  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1175  p_oldXYBoundingRect.bottom() + deltaY);
1176  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1177  p_XYBoundingRect.bottom());
1178  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1179  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1180  enqueueAction(fill);
1181  }
1182  }
1183  }
1184  // Top side of the visible area is the same (start line has not changed, but
1185  // end line may be different)
1186  else {
1188  transform->setResize(p_XYBoundingRect.width(),
1189  p_XYBoundingRect.height());
1190  enqueueAction(transform);
1191 
1192  if(deltaY < 0) {
1193  QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1194  p_XYBoundingRect.bottom() + deltaY);
1195  QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1196  p_XYBoundingRect.bottom());
1197  QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1198  ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1199  enqueueAction(fill);
1200  }
1201  }
1202 
1203  doQueuedActions();
1204  }
1205 
1206 
1212  void ViewportBuffer::addStretchAction() {
1213  for(int i = 0; i < p_actions->size(); i++) {
1214  if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
1215  p_actions->removeAt(i);
1216  i --;
1217  }
1218  }
1219 
1220  enqueueAction(new ViewportBufferStretch());
1221  doQueuedActions();
1222  }
1223 
1233  void ViewportBuffer::emptyBuffer(bool force) {
1234  if(force) {
1235  p_buffer.clear();
1236  p_bufferInitialized = false;
1237  }
1238  }
1239 
1240 
1249  QRect ViewportBuffer::bufferXYRect() {
1250  QRect rect = p_XYBoundingRect;
1251 
1252  if(!rect.height() || !p_buffer.size())
1253  return QRect();
1254 
1255  if(rect.height() > (int) p_buffer.size())
1256  rect.setBottom(rect.top() + p_buffer.size() - 1);
1257 
1258  if(rect.width() > (int) p_buffer[0].size())
1259  rect.setRight(rect.left() + p_buffer[0].size() - 1);
1260 
1261  return rect;
1262  }
1263 
1264 
1273  void ViewportBuffer::scaleChanged() {
1274  if(!p_enabled)
1275  return;
1276 
1277  try {
1278  updateBoundingRects();
1279  reinitialize();
1280  }
1281  catch (IException &e) {
1282  throw IException(
1283  e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
1284  }
1285  }
1286 
1287 
1294  void ViewportBuffer::enable(bool enabled) {
1295  bool wasEnabled = p_enabled;
1296 
1297  p_enabled = enabled;
1298 
1299  if(!wasEnabled && p_enabled) {
1300  updateBoundingRects();
1301  reinitialize();
1302  }
1303  }
1304 
1305 
1311  void ViewportBuffer::setBand(int band) {
1312  if(p_band == band)
1313  return;
1314  p_band = band;
1315 
1316  updateBoundingRects();
1317 
1318  if(!p_enabled)
1319  return;
1320 
1321  reinitialize();
1322  }
1323 
1324 
1332  void ViewportBuffer::reinitialize() {
1333 
1334  try {
1335  // If we're in the middle of a process, we got an okay stretch on startup,
1336  // then we can stop what we're doing.
1337  if(working() && p_initialStretchDone) {
1338  // We only need to handle the current action, can ignore others
1339  ViewportBufferAction *curAction = p_actions->head();
1340 
1341  // Delete older actions
1342  for(int i = p_actions->size() - 1; i > 0; i--) {
1343  delete(*p_actions)[i];
1344  p_actions->pop_back();
1345  }
1346 
1347  // Deal with current action
1348  if(curAction->started()) {
1349  if(curAction->getActionType() == ViewportBufferAction::fill) {
1350  ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
1351 
1352  fill->stop();
1353 
1354  p_requestedFillArea = fill->getRect()->height() *
1355  fill->getRect()->width();
1356  }
1357  }
1358  else {
1359  delete curAction;
1360  p_actions->clear();
1361  p_requestedFillArea = 0.0;
1362  }
1363  }
1364 
1365 
1366  p_bufferInitialized = true;
1367 
1369  reset->setResize(0, 0);
1370  enqueueAction(reset);
1371 
1372  if (p_XYBoundingRect.isValid()) {
1374  transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
1375  enqueueAction(transform);
1376  ViewportBufferFill *fill = createViewportBufferFill(p_XYBoundingRect,
1377  false);
1378  enqueueAction(fill);
1379  }
1380 
1381  doQueuedActions();
1382  }
1383  catch (IException &e) {
1384  throw IException(IException::Programmer,
1385  "Unable to resize and fill buffer.",
1386  _FILEINFO_);
1387  }
1388  }
1389 }