Isis 3 Programmer Reference
ViewportBuffer.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "ViewportBuffer.h"
10#include "ViewportBufferAction.h"
11#include "ViewportBufferStretch.h"
12#include "ViewportBufferFill.h"
13#include "ViewportBufferTransform.h"
14
15#include <QApplication>
16#include <QQueue>
17#include <QRect>
18#include <QScrollBar>
19
20#include "Brick.h"
21#include "CubeDataThread.h"
22#include "CubeViewport.h"
23#include "SpecialPixel.h"
24#include "PixelType.h"
25
26
27#define round(x) ((x) > 0.0 ? (x) + 0.5 : (x) - 0.5)
28
29
30using namespace std;
31
32
33namespace Isis {
43 CubeDataThread *cubeData,
44 int cubeId) {
45 p_dataThread = cubeData;
46 p_cubeId = cubeId;
47
48 p_actions = 0;
49
50 p_actions = new QQueue< ViewportBufferAction * >();
51 p_viewport = viewport;
52 p_bufferInitialized = false;
53 p_band = -1;
54 p_enabled = true;
56 p_viewportHeight = p_viewport->viewport()->height();
57 p_oldViewportHeight = p_viewport->viewport()->height();
58 p_vertScrollBarPos = p_viewport->verticalScrollBar()->value();
59 p_oldVertScrollBarPos = p_viewport->verticalScrollBar()->value();
60
62 p_bricksOrdered = true;
63
64 connect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
65 p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
66
67 connect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
68 this, SLOT(DataReady(void *, int, const Isis::Brick *)));
69
70 connect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
71 p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
72 }
73
79 disconnect(this, SIGNAL(ReadCube(int, int, int, int, int, int, void *)),
80 p_dataThread, SLOT(ReadCube(int, int, int, int, int, int, void *)));
81
82 disconnect(p_dataThread, SIGNAL(ReadReady(void *, int, const Isis::Brick *)),
83 this, SLOT(DataReady(void *, int, const Isis::Brick *)));
84
85 disconnect(this, SIGNAL(DoneWithData(int, const Isis::Brick *)),
86 p_dataThread, SLOT(DoneWithData(int, const Isis::Brick *)));
87
88 p_dataThread = NULL;
89
90 if(p_actions) {
91 while(!p_actions->empty()) {
92 ViewportBufferAction *action = p_actions->dequeue();
93 if(action) {
94 delete action;
95 action = NULL;
96 }
97 }
98 delete p_actions;
99 p_actions = NULL;
100 }
101
102 emptyBuffer(true);
103 }
104
105
112 void ViewportBuffer::fillBuffer(QRect rect) {
113 if(p_band == -1) {
114 throw IException(IException::Programmer, "invalid band", _FILEINFO_);
115 }
116
118 rect.intersected(bufferXYRect()), false);
119 enqueueAction(newFill);
121 }
122
123
131 void ViewportBuffer::fillBuffer(QRect rect, const Brick *data) {
132 if(p_band == -1) {
133 throw IException(IException::Programmer, "invalid band", _FILEINFO_);
134 }
135
136 rect = rect.intersected(bufferXYRect());
137
138 if (!rect.isValid())
139 return;
140
141 try {
142 ViewportBufferFill *fill = createViewportBufferFill(rect, false);
143
144 while(fill->shouldRequestMore()) {
145 fill->incRequestPosition();
146 fill->incReadPosition();
147
148 for(int x = rect.left(); x <= rect.right(); x ++) {
149 // Index into internal buffer is minus leftmost/topmost pixel
150 int xIndex = x - fill->getLeftmostPixelPosition();
151 int yIndex = fill->getRequestPosition() -
153
154 double samp = fill->viewportToSample(x);
155 double line = fill->viewportToLine(fill->getRequestPosition());
156 if (samp < data->Sample())
157 samp = data->Sample();
158 if (samp > data->Sample() + data->SampleDimension())
159 samp = data->Sample() + data->SampleDimension();
160 if (line < data->Line())
161 line = data->Line();
162 if (line > data->Line() + data->LineDimension())
163 line = data->Line() + data->LineDimension();
164
165 // Index into buffer is current sample - start sample
166 // *Brick indices are in units of cube pixels, not screen pixels
167 int brickIndex = data->Index((int)(samp + 0.5), (int)(line + 0.5), p_band);
168
169 if(brickIndex < 0) {
170 p_buffer.at(yIndex).at(xIndex) = data->at(0);
171 }
172 else if(brickIndex >= data->size()) {
173 p_buffer.at(yIndex).at(xIndex) = data->at(data->size() - 1);
174 }
175 else {
176 if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
177 xIndex >= (int) p_buffer.at(yIndex).size()) {
179 "index out of range",
180 _FILEINFO_);
181 }
182 else {
183 p_buffer.at(yIndex).at(xIndex) = data->at(brickIndex);
184 }
185 }
186 }
187 }
188 }
189 catch (IException &e) {
190 throw IException(e, IException::Programmer, "Failed to load brick "
191 "into buffer", _FILEINFO_);
192 }
193 }
194
195
205 void ViewportBuffer::DataReady(void *requester, int cubeId,
206 const Brick *brick) {
207 if(this != requester)
208 return;
209
210 if(p_actions->empty()) {
211 throw IException(IException::Programmer, "no actions", _FILEINFO_);
212 }
213
214 ViewportBufferAction *curAction = p_actions->head();
215
216 if(curAction->getActionType() != ViewportBufferAction::fill ||
217 !curAction->started()) {
218 throw IException(IException::Programmer, "not a fill action", _FILEINFO_);
219 }
220
221 ViewportBufferFill *fill = (ViewportBufferFill *) curAction;
222
223 const QRect *rect = fill->getRect();
224
225 int y = fill->getReadPosition(); // screen line
226
227 // check to see if the next screen line's brick differs from this screen
228 // line's brick. If it does, which brick do we use?
229 int curBrickLine = (int) (fill->viewportToLine(y) + 0.5);
230 int nextBrickLine = (int) (fill->viewportToLine(y + 1) + 0.5);
231 bool brickOrderCorrection = p_bricksOrdered;
232 if (curBrickLine != nextBrickLine &&
233 nextBrickLine == (int) (brick->Line() + 0.5)) {
234 y++;
235 p_bricksOrdered = false;
236 }
237 else {
238 p_bricksOrdered = true;
239 }
240
241 double samp;
242
243 // Loop through x values of rect on screen that we want to fill
244 for(int x = rect->left(); x <= rect->right(); x++) {
245 // Index into internal buffer is minus leftmost/topmost pixel
246 int xIndex = x - fill->getLeftmostPixelPosition();
247 int yIndex = y - fill->getTopmostPixelPosition();
248
249 samp = fill->viewportToSample(x);
250
251 // Index into buffer is current sample - start sample
252 // *Brick indices are in units of cube pixels, not screen pixels
253 int brickIndex = (int)(samp + 0.5) - brick->Sample();
254
255 if(brickIndex < 0) {
256 p_buffer.at(yIndex).at(xIndex) = brick->at(0);
257 }
258 else if(brickIndex >= brick->size()) {
259 p_buffer.at(yIndex).at(xIndex) = brick->at(brick->size() - 1);
260 }
261 else {
262 if(yIndex < 0 || xIndex < 0 || yIndex >= (int) p_buffer.size() ||
263 xIndex >= (int) p_buffer.at(yIndex).size()) {
264 IString msg = "An index out of range error was detected. ";
265
266 if(yIndex < 0)
267 msg += "The Y-Index [" + IString(yIndex) + "] is less than 0";
268 else if(xIndex < 0)
269 msg += "The X-Index [" + IString(xIndex) + "] is less than 0";
270 else if(yIndex > (int)p_buffer.size())
271 msg += "The Y-Index [" + IString(yIndex) + "] is greater than the "
272 "Y-Size of [" + IString((int)p_buffer.size()) + "]";
273 else if(xIndex > (int)p_buffer.at(yIndex).size())
274 msg += "The X-Index [" + IString(xIndex) + " is greater than the "
275 "X-Size of [" + IString((int) p_buffer.at(yIndex).size()) + "]";
276
277 throw IException(IException::Programmer, msg, _FILEINFO_);
278 }
279 else {
280 p_buffer.at(yIndex).at(xIndex) = brick->at(brickIndex);
281 }
282 }
283 }
284 fill->incReadPosition();
285
286
287 if(fill->shouldRequestMore()) {
288 if (p_bricksOrdered) {
289 requestCubeLine(fill);
290 }
291 else {
292 if (brickOrderCorrection) {
293 requestCubeLine(fill);
294 requestCubeLine(fill);
295 }
296 }
297 }
298 else if(fill->doneReading()) {
299 delete fill;
300 fill = NULL;
301 p_actions->dequeue();
303 }
304
305 emit DoneWithData(cubeId, brick);
306 }
307
308
318 if(action->getActionType() == ViewportBufferAction::fill) {
319 QRect *fillRect = ((ViewportBufferFill *)action)->getRect();
320 p_requestedFillArea += fillRect->width() * fillRect->height();
321 }
322
323 if(p_actions->empty()) {
325 }
326
327 p_actions->enqueue(action);
328 }
329
330
339 const vector<double> &ViewportBuffer::getLine(int line) {
341 throw IException(IException::Programmer, "no data", _FILEINFO_);
342 }
343
344 if(line < 0 || line >= (int)p_buffer.size()) {
346 "Invalid call to getLine",
347 _FILEINFO_);
348 }
349
350 return p_buffer.at(line);
351 }
352
353
362 int startx, starty, endx, endy;
363 p_viewport->cubeToViewport(0.5, 0.5, startx, starty);
364
365 // Handle case where x,y 0,0 is sample,line 0,0 (which is outside the cube)
366 // and cubeToViewport still tells us 0.5, 0.5 is at x,y 0,0.
367 double startSamp, startLine;
368 p_viewport->viewportToCube(startx, starty, startSamp, startLine);
369
370 if(startSamp < 0.5)
371 startx ++;
372
373 if(startLine < 0.5)
374 starty ++;
375
376 double rightmost = p_viewport->cubeSamples() + 0.5;
377 double bottommost = p_viewport->cubeLines() + 0.5;
378
379 p_viewport->cubeToViewport(rightmost, bottommost, endx, endy);
380
381 if(endx < 0 || endy < 0)
382 return QRect();
383
384 double endSamp = -1, endLine = -1;
385 p_viewport->viewportToCube(endx, endy, endSamp, endLine);
386
387 if(endSamp > rightmost)
388 endx --;
389
390 if(endLine > bottommost)
391 endy --;
392
393 // Make sure our rect makes sense
394 if(startx < 0) {
395 startx = 0;
396 }
397
398 if(starty < 0) {
399 starty = 0;
400 }
401
402 if(endx >= p_viewport->viewport()->width()) {
403 endx = p_viewport->viewport()->width() - 1;
404 }
405
406 if(endy >= p_viewport->viewport()->height()) {
407 endy = p_viewport->viewport()->height() - 1;
408 }
409
410 return QRect(startx, starty, endx - startx + 1, endy - starty + 1);
411 }
412
413
421 double sampTolerance = 0.05 * p_viewport->cubeSamples();
422 double lineTolerance = 0.05 * p_viewport->cubeLines();
423
424 bool hasCube = true;
425
426 hasCube &= !working();
427 hasCube &= p_sampLineBoundingRect[rectLeft] <= (1 + sampTolerance);
428 hasCube &= p_sampLineBoundingRect[rectTop] <= (1 + lineTolerance);
430 sampTolerance);
432 lineTolerance);
433 return hasCube;
434 }
435
436
445 QRect xyRect = getXYBoundingRect();
446 double ssamp, esamp, sline, eline;
447 p_viewport->viewportToCube(xyRect.left(), xyRect.top(), ssamp, sline);
448 p_viewport->viewportToCube(xyRect.right(), xyRect.bottom(), esamp, eline);
449
450 QList<double> boundingRect;
451
452 boundingRect.insert(rectLeft, ssamp);
453 boundingRect.insert(rectTop, sline);
454 boundingRect.insert(rectRight, esamp);
455 boundingRect.insert(rectBottom, eline);
456
457 return boundingRect;
458 }
459
460
479
480
494 QRect someRect, bool useOldY) {
495 QScrollBar *hsb = p_viewport->horizontalScrollBar();
496 int xConstCoef = hsb->value();
497 xConstCoef -= p_viewport->viewport()->width() / 2;
498
499 // If panning over a full screen, it will try to create a fill rect that
500 // isn't actually valid. So, in the case of any bad fill rects we will
501 // fill everything.
502 if(!someRect.isValid()) {
503 throw IException(IException::Programmer, "Fill rect invalid", _FILEINFO_);
504 }
505
506 double xScale = p_viewport->scale();
507
508 int yConstCoef = 0;
509
510 if(!useOldY)
511 yConstCoef = (p_vertScrollBarPos) - p_viewportHeight / 2 - 1;
512 else
513 yConstCoef = (p_oldVertScrollBarPos) - p_oldViewportHeight / 2 - 1;
514
515 double yScale = xScale;
516
517 QPoint topLeft;
518
519 if(!useOldY) {
520 topLeft = p_XYBoundingRect.topLeft();
521 }
522 else {
523 topLeft = QPoint(p_XYBoundingRect.left(), p_oldXYBoundingRect.top());
524 }
525
526 ViewportBufferFill *newFill = new ViewportBufferFill(someRect, xConstCoef,
527 xScale, yConstCoef, yScale, topLeft);
528
529 return newFill;
530 }
531
532
539 if(p_band == -1) {
540 throw IException(IException::Programmer, "invalid band", _FILEINFO_);
541 }
542
543 // Prep to create minimal buffer(s) to read the cube
544 QRect &rect = *fill->getRect();
545
546 double ssamp = fill->viewportToSample(rect.left());
547
548 double esamp = fill->viewportToSample(rect.right());
549
550 int brickWidth = (int)(ceil(esamp) - floor(ssamp)) + 1;
551
552 if(brickWidth <= 0)
553 return;
554
555 double line = fill->viewportToLine(fill->getRequestPosition());
556 int roundedSamp = (int)(ssamp + 0.5);
557 int roundedLine = (int)(line + 0.5);
558
559 emit ReadCube(p_cubeId, roundedSamp, roundedLine, roundedSamp + brickWidth,
560 roundedLine, p_band, this);
561
562 fill->incRequestPosition();
563 }
564
565
577 bool doNextAction = false;
578
579 ViewportBufferAction *curAction = NULL;
580
581 // if we aren't preserving data, and we don't still need the initial
582 // stretch (on startup), let's reset the buffer.
585 // Actions don't preserve data - call reinitialize!
586 reinitialize();
587 }
588
589 if(!working()) {
591 }
592
593 if(!p_actions->empty()) {
594 curAction = p_actions->head();
595 doNextAction = !curAction->started();
596 }
597
598 while(doNextAction) {
599 if(curAction->getActionType() == ViewportBufferAction::transform) {
601 }
602 else if(curAction->getActionType() == ViewportBufferAction::fill) {
603 startFillAction((ViewportBufferFill *) curAction);
604 }
605 else {
608 }
609
610 doNextAction = !p_actions->empty();
611
612 if(doNextAction) {
613 curAction = p_actions->head();
614 doNextAction = !curAction->started();
615 }
616 }
617
618 if(p_actions->empty()) {
619 // Buffer Updated - Giving it BufferXYRect
621 }
622 }
623
624
631 if(!working())
632 return 1.0;
633 if(p_requestedFillArea <= 0.0)
634 return 0.0;
635
636 return 1.0 - totalUnfilledArea() / p_requestedFillArea;
637 }
638
639
647 double totalFillArea = 0.0;
648
649 // If at any time the total X or Y shift exceeds the buffer size, we aren't
650 // preserving data. Check to see if this is the case!
651 for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
652 ViewportBufferAction *action = (*p_actions)[actionIndex];
653
654 if(action->getActionType() == ViewportBufferAction::fill) {
655 ViewportBufferFill *fill = (ViewportBufferFill *)action;
656
657 QRect unfilledRect(*fill->getRect());
658 unfilledRect.setTop(fill->getReadPosition());
659 totalFillArea += unfilledRect.width() * unfilledRect.height();
660 }
661 }
662
663 return totalFillArea;
664 }
665
666
672 int totalXShift = 0;
673 int totalYShift = 0;
674
675 QRect currentBufferRect(bufferXYRect());
676
677 int bufferWidth = currentBufferRect.width();
678 int bufferHeight = currentBufferRect.height();
679
680 // If at any time the total X or Y shift exceeds the buffer size, we aren't
681 // preserving data. Check to see if this is the case!
682 for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
683 ViewportBufferAction *action = (*p_actions)[actionIndex];
684
685 if(action->getActionType() == ViewportBufferAction::transform) {
687
688 if(transform->resizeFirst()) {
689 bufferWidth = transform->getBufferWidth();
690 bufferHeight = transform->getBufferHeight();
691 }
692
693 if(abs(totalXShift) >= bufferWidth)
694 return false;
695 if(abs(totalYShift) >= bufferHeight)
696 return false;
697
698 // Without the absolute value this will calculate
699 // if any data on the screen is preserved, however
700 // a better method is to see if its quicker to reread
701 // it all which happens when we use abs
702 totalXShift += abs(transform->getXTranslation());
703 totalYShift += abs(transform->getYTranslation());
704
705 if(!transform->resizeFirst()) {
706 bufferWidth = transform->getBufferWidth();
707 bufferHeight = transform->getBufferHeight();
708 }
709
710 if(abs(totalXShift) >= bufferWidth)
711 return false;
712 if(abs(totalYShift) >= bufferHeight)
713 return false;
714 }
715 }
716
717 return true;
718 }
719
720
728 QRect currentBufferRect(bufferXYRect());
729
730 if(currentBufferRect.width() == 0 || currentBufferRect.height() == 0) {
731 return true;
732 }
733
734 for(int actionIndex = 0; actionIndex < p_actions->size(); actionIndex ++) {
735 ViewportBufferAction *action = (*p_actions)[actionIndex];
736
737 if(action->getActionType() == ViewportBufferAction::transform) {
739
740 if(transform->getBufferWidth() == 0)
741 return true;
742 if(transform->getBufferHeight() == 0)
743 return true;
744 }
745 }
746
747 return false;
748 }
749
750
757 bool hasFillAction = false;
758 for (auto action : *p_actions) {
759 if (action->getActionType() == ViewportBufferAction::fill) {
760 hasFillAction = true;
761 break;
762 }
763 }
764 return hasFillAction || !p_bufferInitialized || !p_enabled;
765 }
766
767
774 bool needResize = action->getBufferWidth() > -1 &&
775 action->getBufferHeight() > -1;
776
777 if(action->resizeFirst() && needResize) {
778 resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
779 }
780
781 shiftBuffer(action->getXTranslation(), action->getYTranslation());
782
783 if(!action->resizeFirst() && needResize) {
784 resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
785 }
786
787 delete action;
788 action = NULL;
789 p_actions->dequeue();
790 }
791
792
800 if(action->started())
801 return;
802
803 action->started(true);
804
805 requestCubeLine(action);
806
807 if(action->shouldRequestMore()) {
808 requestCubeLine(action);
809 }
810 }
811
812
819 delete action;
820 action = NULL;
821 p_actions->dequeue();
822
823 p_viewport->restretch(this);
824 }
825
826
833 void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
834 p_buffer.resize(height);
835
836 for(unsigned int i = 0; i < p_buffer.size(); i++) {
837 p_buffer[i].resize(width, Null);
838 }
839 }
840
841
849 void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
850 if(deltaY >= 0) {
851 for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
852 p_buffer[i] = p_buffer[i - deltaY];
853
854 // If we have y shift, null out original data (keep only moved data)
855 if(deltaY != 0) {
856 for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
857 p_buffer[i - deltaY][x] = Null;
858 }
859 }
860
861 if(deltaX > 0) {
862 for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
863 p_buffer[i][j] = p_buffer[i][j - deltaX];
864 p_buffer[i][j - deltaX] = Null;
865 }
866 }
867 else if(deltaX < 0) {
868 for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
869 p_buffer[i][j] = p_buffer[i][j - deltaX];
870 p_buffer[i][j - deltaX] = Null;
871 }
872 }
873 }
874 }
875 else if(deltaY < 0) {
876 for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
877 p_buffer[i] = p_buffer[i - deltaY];
878
879 // null out original data (keep only moved data)
880 for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
881 p_buffer[i - deltaY][x] = Null;
882 }
883
884 if(deltaX > 0) {
885 for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
886 p_buffer[i][j] = p_buffer[i][j - deltaX];
887 p_buffer[i][j - deltaX] = Null;
888 }
889 }
890 else if(deltaX < 0) {
891 for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
892 p_buffer[i][j] = p_buffer[i][j - deltaX];
893 p_buffer[i][j - deltaX] = Null;
894 }
895 }
896 }
897 }
898 }
899
900
907
909 return;
910
911 // ensure we have a valid bounding rect! For example, If the cube viewport
912 // is hidden and then shown again this could happen.
913 if(!p_XYBoundingRect.isValid())
914 return;
915
916 if(!p_oldXYBoundingRect.isValid()) {
917 reinitialize();
918 return;
919 }
920
921 //We need to know how much data was gained/lost on each side of the cube
922 double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
924 //The input to round should be close to an integer
925 int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
926
927 double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
929 int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
930
931 double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
933 int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
934
935 double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
937 int deltaBottomPixels = (int)round(deltaBottomSamples *
938 p_viewport->scale());
939
940 //deltaW is the change in width in the visible area of the cube
941 int deltaW = - deltaLeftPixels + deltaRightPixels;
942
943 //deltaH is the change in height in the visible area of the cube
944 int deltaH = - deltaTopPixels + deltaBottomPixels;
945
946 //If the new visible width has changed (resized in the horizontal direction)
947 if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
948 //Resized larger in the horizontal direction
949 if(deltaW > 0) {
950 //Using old height because we might lose data if new height is smaller
952 transform->setTranslation(-deltaLeftPixels, 0);
953 transform->setResize(p_XYBoundingRect.width(),
954 p_oldXYBoundingRect.height());
955 transform->resizeFirst(true);
956
957 enqueueAction(transform);
958
959 // left side that needs filled
960 QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
961 p_oldXYBoundingRect.top());
962
963 QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
964 p_oldXYBoundingRect.bottom());
965
966 QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
967
968 ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
969 true);
970 enqueueAction(leftFill);
971
972 // right side that needs filled
973 QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
974 p_oldXYBoundingRect.top());
975
976 QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
977 p_oldXYBoundingRect.bottom());
978
979 QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
980
981 ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
982 true);
983 enqueueAction(rightFill);
984 }
985 //Resized smaller in the horizontal direction
986 else if(deltaW < 0) {
988 transform->setTranslation(-deltaLeftPixels, 0);
989 transform->setResize(p_XYBoundingRect.width(),
990 p_oldXYBoundingRect.height());
991 transform->resizeFirst(false);
992 enqueueAction(transform);
993 }
994 }
995
996 //If the new visible height has changed (resized in the vertical direction)
997 if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
998 //Resized larger in the vertical direction
999 if(deltaH > 0) {
1001 transform->setTranslation(0, -deltaTopPixels);
1002 transform->setResize(p_XYBoundingRect.width(),
1003 p_XYBoundingRect.height());
1004 transform->resizeFirst(true);
1005
1006 enqueueAction(transform);
1007
1008 QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
1009 p_XYBoundingRect.top() - deltaTopPixels);
1010
1011 QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
1012
1013
1014 QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
1015 p_XYBoundingRect.bottom() -
1016 deltaBottomPixels);
1017
1018 QRect bottomSideToFill(topLeftOfbottomSide,
1019 p_XYBoundingRect.bottomRight());
1020
1021 ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
1022 false);
1023 enqueueAction(topFill);
1024
1025 ViewportBufferFill *bottomFill =
1026 createViewportBufferFill(bottomSideToFill, false);
1027 enqueueAction(bottomFill);
1028 }
1029 //Resized smaller in the vertical direction
1030 else if(deltaH < 0) {
1032
1033 transform->setTranslation(0, -deltaTopPixels);
1034 transform->setResize(p_XYBoundingRect.width(),
1035 p_oldXYBoundingRect.height());
1036
1037 enqueueAction(transform);
1038 }
1039 }
1040
1042 }
1043
1044
1052 void ViewportBuffer::pan(int deltaX, int deltaY) {
1054
1056 return;
1057 }
1058
1059
1061 //The sample line bounding rect contains the bounds of the
1062 //screen pixels to the cube sample/line bounds. If the cube
1063 //bounds do not change, then we do not need to do anything to
1064 //the buffer.
1065 return;
1066 }
1067
1068 double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
1070 int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
1071
1072 double deltaTopLines = p_sampLineBoundingRect[rectTop] -
1074 int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
1075
1076 // Don't try to figure out panning beyond a full screen,
1077 // even though data could very well be preserved.
1078 if(abs(deltaY) >= p_XYBoundingRect.height() ||
1079 abs(deltaX) >= p_XYBoundingRect.width()) {
1080 reinitialize();
1081 return;
1082 }
1083
1084 //Left side of the visible area changed (start sample is different)
1086 //Shifting data to the right
1087 if(deltaX > 0) {
1088 // The buffer is getting bigger
1090 transform->setResize(p_XYBoundingRect.width(),
1091 p_oldXYBoundingRect.height());
1092 transform->setTranslation(-deltaLeftPixels, 0);
1093 transform->resizeFirst(true);
1094
1095 enqueueAction(transform);
1096
1097 QPoint topLeftOfRefill(p_XYBoundingRect.left(),
1098 p_oldXYBoundingRect.top());
1099
1100 QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
1101 p_oldXYBoundingRect.bottom());
1102 QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
1103
1104 ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1105 enqueueAction(fill);
1106 }
1107 //Shifting data to the left
1108 else if(deltaX < 0) {
1109 //The buffer is getting smaller - no new data
1111 transform->setTranslation(-deltaLeftPixels, 0);
1112 transform->setResize(p_XYBoundingRect.width(),
1113 p_oldXYBoundingRect.height());
1114 transform->resizeFirst(false);
1115 enqueueAction(transform);
1116
1117 // if new samples on the screen at all, mark it for reading
1120 QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
1121 p_oldXYBoundingRect.top());
1122 QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
1123 p_oldXYBoundingRect.bottom());
1124
1125 QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
1126
1128 true);
1129 enqueueAction(fill);
1130 }
1131 }
1132 }
1133 // Left side of the visible area is the same (start sample has not changed,
1134 // but end sample may be different)
1135 else {
1137 transform->setResize(p_XYBoundingRect.width(),
1138 p_oldXYBoundingRect.height());
1139 enqueueAction(transform);
1140
1141 if(deltaX < 0) {
1142 QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
1143 p_oldXYBoundingRect.top());
1144
1145 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1146 p_oldXYBoundingRect.bottom());
1147
1148 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1149 ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1150
1151 enqueueAction(fill);
1152 }
1153 }
1154
1155 //Top side of the visible area changed (start line is different)
1157 //Shifting data down
1158 if(deltaY > 0) {
1160 transform->setTranslation(0, -deltaTopPixels);
1161 transform->setResize(p_XYBoundingRect.width(),
1162 p_XYBoundingRect.height());
1163 transform->resizeFirst(true);
1164 enqueueAction(transform);
1165
1166 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1167 p_XYBoundingRect.top());
1168 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1169 p_XYBoundingRect.top() + deltaY);
1170 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1171 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1172 enqueueAction(fill);
1173 }
1174 //Shifting data up
1175 else if(deltaY < 0) {
1177 transform->setTranslation(0, -deltaTopPixels);
1178 transform->setResize(p_XYBoundingRect.width(),
1179 p_XYBoundingRect.height());
1180 transform->resizeFirst(false);
1181 enqueueAction(transform);
1182
1183 // if new lines on the screen at all, mark it for reading
1186 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1187 p_oldXYBoundingRect.bottom() + deltaY);
1188 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1189 p_XYBoundingRect.bottom());
1190 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1191 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1192 enqueueAction(fill);
1193 }
1194 }
1195 }
1196 // Top side of the visible area is the same (start line has not changed, but
1197 // end line may be different)
1198 else {
1200 transform->setResize(p_XYBoundingRect.width(),
1201 p_XYBoundingRect.height());
1202 enqueueAction(transform);
1203
1204 if(deltaY < 0) {
1205 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1206 p_XYBoundingRect.bottom() + deltaY);
1207 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1208 p_XYBoundingRect.bottom());
1209 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1210 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1211 enqueueAction(fill);
1212 }
1213 }
1214
1216 }
1217
1218
1225 for(int i = 0; i < p_actions->size(); i++) {
1226 if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
1227 p_actions->removeAt(i);
1228 i --;
1229 }
1230 }
1231
1234 }
1235
1246 if(force) {
1247 p_buffer.clear();
1248 p_bufferInitialized = false;
1249 }
1250 }
1251
1252
1262 QRect rect = p_XYBoundingRect;
1263
1264 if(!rect.height() || !p_buffer.size())
1265 return QRect();
1266
1267 if(rect.height() > (int) p_buffer.size())
1268 rect.setBottom(rect.top() + p_buffer.size() - 1);
1269
1270 if(rect.width() > (int) p_buffer[0].size())
1271 rect.setRight(rect.left() + p_buffer[0].size() - 1);
1272
1273 return rect;
1274 }
1275
1276
1286 if(!p_enabled)
1287 return;
1288
1289 try {
1291 reinitialize();
1292 }
1293 catch (IException &e) {
1294 throw IException(
1295 e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
1296 }
1297 }
1298
1299
1306 void ViewportBuffer::enable(bool enabled) {
1307 bool wasEnabled = p_enabled;
1308
1310
1311 if(!wasEnabled && p_enabled) {
1313 reinitialize();
1314 }
1315 }
1316
1317
1324 if(p_band == band)
1325 return;
1326 p_band = band;
1327
1329
1330 if(!p_enabled)
1331 return;
1332
1333 reinitialize();
1334 }
1335
1336
1345
1346 try {
1347 // If we're in the middle of a process, we got an okay stretch on startup,
1348 // then we can stop what we're doing.
1349 if(working() && p_initialStretchDone) {
1350 // We only need to handle the current action, can ignore others
1351 ViewportBufferAction *curAction = p_actions->head();
1352
1353 // Delete older actions
1354 for(int i = p_actions->size() - 1; i > 0; i--) {
1355 delete(*p_actions)[i];
1356 p_actions->pop_back();
1357 }
1358
1359 // Deal with current action
1360 if(curAction->started()) {
1361 if(curAction->getActionType() == ViewportBufferAction::fill) {
1362 ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
1363
1364 fill->stop();
1365
1366 p_requestedFillArea = fill->getRect()->height() *
1367 fill->getRect()->width();
1368 }
1369 }
1370 else {
1371 delete curAction;
1372 p_actions->clear();
1373 p_requestedFillArea = 0.0;
1374 }
1375 }
1376
1377
1378 p_bufferInitialized = true;
1379
1381 reset->setResize(0, 0);
1382 enqueueAction(reset);
1383
1384 if (p_XYBoundingRect.isValid()) {
1386 transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
1387 enqueueAction(transform);
1389 false);
1390 enqueueAction(fill);
1391 }
1392
1394 }
1395 catch (IException &e) {
1397 "Unable to resize and fill buffer.",
1398 _FILEINFO_);
1399 }
1400 }
1401}
Buffer for containing a three dimensional section of an image.
Definition Brick.h:45
int size() const
Returns the total number of pixels in the shape buffer.
Definition Buffer.h:97
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition Buffer.cpp:145
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition Buffer.h:79
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition Buffer.h:70
double at(const int index) const
Returns the value in the shape buffer at the given index.
Definition Buffer.cpp:231
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
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition Buffer.cpp:127
Encapsulation of Cube I/O with Change Notifications.
Widget to display Isis cubes for qt apps.
double scale() const
int cubeLines() const
Return the number of lines in the cube.
virtual void restretch(ViewportBuffer *buffer)=0
This is called by internal viewport buffers when a stretch action should be performed.
int cubeSamples() const
Return the number of samples in the cube.
void viewportToCube(int x, int y, double &sample, double &line) const
Turns a viewport into a cube.
void cubeToViewport(double sample, double line, int &x, int &y) const
Turns a cube into a viewport.
void enableProgress()
This restarts the progress bar.
void bufferUpdated(QRect rect)
This method is called by ViewportBuffer upon successful completion of all operations and gives the ap...
Isis exception class.
Definition IException.h:91
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
Adds specific functionality to C++ strings.
Definition IString.h:165
@ transform
ViewportBufferTransform.
@ stretch
ViewportBufferStretch.
double viewportToLine(int y)
Converts screen y position to cube line position.
int getRequestPosition() const
Returns the current request position (>= read position)
int getTopmostPixelPosition()
Returns the top of the X/Y bounding rect for this fill.
int getLeftmostPixelPosition()
Returns the left of the X/Y bounding rect for this fill.
void incRequestPosition()
Increment request position.
void stop()
Cancels the current operation.
void incReadPosition()
Increment read position.
int getReadPosition() const
Returns the current read position.
QRect * getRect()
Returns the rect that this action is filling in screen pixels.
bool shouldRequestMore()
Returns true if request position is past the end of the fill.
bool doneReading()
Returns true if read position is past the end of the fill.
double viewportToSample(int x)
Converts screen x position to cube sample position.
QList< double > p_sampLineBoundingRect
This rect is in cube pixels and represents the area that this viewport buffer defines in the viewport...
void enqueueAction(ViewportBufferAction *)
This enqueues the given action.
void addStretchAction()
When all current operations finish the cube viewport will be asked to do a stretch if you call this.
bool actionsPreserveData()
This returns true if any data currently in the buffer would be preserved if all of the queued actions...
void startFillAction(ViewportBufferFill *action)
Initializes a fill action by requesting the initial cube data.
void DoneWithData(int, const Isis::Brick *)
Tell cube data thread we're done with a brick.
int p_vertScrollBarPos
Current vertical scroll bar position.
CubeViewport * p_viewport
The CubeViewport which created this buffer.
const std::vector< double > & getLine(int line)
Retrieves a line from the buffer.
QRect p_oldXYBoundingRect
The previous bounding rect.
QRect getXYBoundingRect()
Retrieves the current bounding rect in viewport pixels of the visible cube area.
int p_band
The band to read from.
void reinitialize()
This resizes and fills entire buffer.
void DataReady(void *requester, int cubeId, const Isis::Brick *brick)
This method is called when requested bricks become available.
double totalUnfilledArea()
This returns the amount of area in the queue that needs new cube data/will be filled by fill actions.
void resizeBuffer(unsigned int width, unsigned int height)
Enlarges or shrinks the buffer and fills with nulls if necessary.
void setBand(int band)
Sets the band to read from, the buffer will be re-read if the band changes.
double currentProgress()
Returns the viewport buffer's loading progress.
void shiftBuffer(int deltaX, int deltaY)
Shifts the DN values in the buffer by deltaX and deltaY.
void ReadCube(int cubeId, int startSample, int startLine, int endSample, int endLine, int band, void *caller)
Ask the cube data thread for data.
void doQueuedActions()
This processes the next available action, or starts processing it, if possible.
void updateBoundingRects()
Sets the old and new bounding rects.
double p_requestedFillArea
Sum of the requested area to be filled.
virtual ~ViewportBuffer()
Updates total buffer size on destruction.
QQueue< ViewportBufferAction * > * p_actions
This is the set of actions we wish to perform on the buffer.
@ rectBottom
QRect.bottom()
@ rectRight
QRect.right()
ViewportBufferFill * createViewportBufferFill(QRect, bool)
This method creates a fill action based on a rect and using new versus old Y values.
int p_oldViewportHeight
Previous viewport height.
bool p_initialStretchDone
True if a stretch action has occurred.
ViewportBuffer(CubeViewport *viewport, CubeDataThread *cubeData, int cubeId)
ViewportBuffer constructor.
void enable(bool enabled)
This turns on or off reading from the cube.
void doStretchAction(ViewportBufferStretch *action)
Tells the cube viewport to restretch.
QList< double > p_oldSampLineBoundingRect
Previous bounding rect.
QList< double > getSampLineBoundingRect()
Retrieves the current bounding rect in sample/line coordinates of the visible cube area.
QRect bufferXYRect()
Returns a rect, in screen pixels, of the area this buffer covers.
bool p_bufferInitialized
True if the buffer has been initialized.
void resizedViewport()
Call this when the viewport is resized (not zoomed).
bool enabled()
Returns whether the buffer is enabled (reading data) or not.
CubeDataThread * p_dataThread
manages cube io
int p_cubeId
Id associated with the cube in this viewport buffer.
int p_oldVertScrollBarPos
Previous vertical scroll bar position.
bool hasEntireCube()
Method to see if the entire cube is in the buffer.
QRect p_XYBoundingRect
This rect is in viewport pixels and represents the area that this viewport buffer defines in the view...
void fillBuffer(QRect rect)
This method will convert the rect to sample/line positions and read from the cube into the buffer.
void emptyBuffer(bool force=false)
This is meant to clear up ram on non-active viewports.
void doTransformAction(ViewportBufferTransform *action)
Does a transformation on the internal viewport buffer.
bool working()
This tests if queued actions exist in the viewport buffer.
bool p_enabled
True if reading from cube (active)
void pan(int deltaX, int deltaY)
Call this when the viewport is panned.
std::vector< std::vector< double > > p_buffer
The buffer to hold cube dn values.
void scaleChanged()
Call this when zoomed, re-reads visible area.
void requestCubeLine(ViewportBufferFill *fill)
This requests the next line in a fill action.
int p_viewportHeight
Current viewport height.
bool reinitializeActionExists()
This searches for actions that will reset the entire buffer's contents.
void setResize(int width, int height)
Sets the size the buffer should be resized to.
int getXTranslation()
Gets the amount the buffer should be translated in X.
int getYTranslation()
Gets the amount the buffer should be translated in Y.
const int & getBufferHeight()
Returns the new buffer height.
bool resizeFirst()
Returns true if the resize should happen before the translation.
const int & getBufferWidth()
Returns the new buffer width.
void setTranslation(int x, int y)
Sets the translation amount in x and y.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
const double Null
Value for an Isis Null pixel.
Namespace for the standard library.