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 return !p_actions->empty() || !p_bufferInitialized || !p_enabled;
758 }
759
760
767 bool needResize = action->getBufferWidth() > -1 &&
768 action->getBufferHeight() > -1;
769
770 if(action->resizeFirst() && needResize) {
771 resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
772 }
773
774 shiftBuffer(action->getXTranslation(), action->getYTranslation());
775
776 if(!action->resizeFirst() && needResize) {
777 resizeBuffer(action->getBufferWidth(), action->getBufferHeight());
778 }
779
780 delete action;
781 action = NULL;
782 p_actions->dequeue();
783 }
784
785
793 if(action->started())
794 return;
795
796 action->started(true);
797
798 requestCubeLine(action);
799
800 if(action->shouldRequestMore()) {
801 requestCubeLine(action);
802 }
803 }
804
805
812 delete action;
813 action = NULL;
814 p_actions->dequeue();
815
816 p_viewport->restretch(this);
817 }
818
819
826 void ViewportBuffer::resizeBuffer(unsigned int width, unsigned int height) {
827 p_buffer.resize(height);
828
829 for(unsigned int i = 0; i < p_buffer.size(); i++) {
830 p_buffer[i].resize(width, Null);
831 }
832 }
833
834
842 void ViewportBuffer::shiftBuffer(int deltaX, int deltaY) {
843 if(deltaY >= 0) {
844 for(int i = p_buffer.size() - 1; i >= deltaY; i--) {
845 p_buffer[i] = p_buffer[i - deltaY];
846
847 // If we have y shift, null out original data (keep only moved data)
848 if(deltaY != 0) {
849 for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
850 p_buffer[i - deltaY][x] = Null;
851 }
852 }
853
854 if(deltaX > 0) {
855 for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
856 p_buffer[i][j] = p_buffer[i][j - deltaX];
857 p_buffer[i][j - deltaX] = Null;
858 }
859 }
860 else if(deltaX < 0) {
861 for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
862 p_buffer[i][j] = p_buffer[i][j - deltaX];
863 p_buffer[i][j - deltaX] = Null;
864 }
865 }
866 }
867 }
868 else if(deltaY < 0) {
869 for(int i = 0; i < (int)p_buffer.size() + deltaY; i++) {
870 p_buffer[i] = p_buffer[i - deltaY];
871
872 // null out original data (keep only moved data)
873 for(unsigned int x = 0; x < p_buffer[i - deltaY].size(); x++) {
874 p_buffer[i - deltaY][x] = Null;
875 }
876
877 if(deltaX > 0) {
878 for(int j = p_buffer[i].size() - 1; j >= deltaX; j--) {
879 p_buffer[i][j] = p_buffer[i][j - deltaX];
880 p_buffer[i][j - deltaX] = Null;
881 }
882 }
883 else if(deltaX < 0) {
884 for(int j = 0; j < (int)p_buffer[i].size() + deltaX; j++) {
885 p_buffer[i][j] = p_buffer[i][j - deltaX];
886 p_buffer[i][j - deltaX] = Null;
887 }
888 }
889 }
890 }
891 }
892
893
900
902 return;
903
904 // ensure we have a valid bounding rect! For example, If the cube viewport
905 // is hidden and then shown again this could happen.
906 if(!p_XYBoundingRect.isValid())
907 return;
908
909 if(!p_oldXYBoundingRect.isValid()) {
910 reinitialize();
911 return;
912 }
913
914 //We need to know how much data was gained/lost on each side of the cube
915 double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
917 //The input to round should be close to an integer
918 int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
919
920 double deltaRightSamples = p_sampLineBoundingRect[rectRight] -
922 int deltaRightPixels = (int)round(deltaRightSamples * p_viewport->scale());
923
924 double deltaTopSamples = p_sampLineBoundingRect[rectTop] -
926 int deltaTopPixels = (int)round(deltaTopSamples * p_viewport->scale());
927
928 double deltaBottomSamples = p_sampLineBoundingRect[rectBottom] -
930 int deltaBottomPixels = (int)round(deltaBottomSamples *
931 p_viewport->scale());
932
933 //deltaW is the change in width in the visible area of the cube
934 int deltaW = - deltaLeftPixels + deltaRightPixels;
935
936 //deltaH is the change in height in the visible area of the cube
937 int deltaH = - deltaTopPixels + deltaBottomPixels;
938
939 //If the new visible width has changed (resized in the horizontal direction)
940 if(p_XYBoundingRect.width() != p_oldXYBoundingRect.width()) {
941 //Resized larger in the horizontal direction
942 if(deltaW > 0) {
943 //Using old height because we might lose data if new height is smaller
945 transform->setTranslation(-deltaLeftPixels, 0);
946 transform->setResize(p_XYBoundingRect.width(),
947 p_oldXYBoundingRect.height());
948 transform->resizeFirst(true);
949
950 enqueueAction(transform);
951
952 // left side that needs filled
953 QPoint topLeftOfLeftRect(p_XYBoundingRect.left(),
954 p_oldXYBoundingRect.top());
955
956 QPoint bottomRightOfLeftRect(p_XYBoundingRect.left() - deltaLeftPixels,
957 p_oldXYBoundingRect.bottom());
958
959 QRect leftRect(topLeftOfLeftRect, bottomRightOfLeftRect);
960
961 ViewportBufferFill *leftFill = createViewportBufferFill(leftRect,
962 true);
963 enqueueAction(leftFill);
964
965 // right side that needs filled
966 QPoint topLeftOfRightRect(p_XYBoundingRect.right() - deltaRightPixels,
967 p_oldXYBoundingRect.top());
968
969 QPoint bottomRightOfRightRect(p_XYBoundingRect.right(),
970 p_oldXYBoundingRect.bottom());
971
972 QRect rightRect(topLeftOfRightRect, bottomRightOfRightRect);
973
974 ViewportBufferFill *rightFill = createViewportBufferFill(rightRect,
975 true);
976 enqueueAction(rightFill);
977 }
978 //Resized smaller in the horizontal direction
979 else if(deltaW < 0) {
981 transform->setTranslation(-deltaLeftPixels, 0);
982 transform->setResize(p_XYBoundingRect.width(),
983 p_oldXYBoundingRect.height());
984 transform->resizeFirst(false);
985 enqueueAction(transform);
986 }
987 }
988
989 //If the new visible height has changed (resized in the vertical direction)
990 if(p_XYBoundingRect.height() != p_oldXYBoundingRect.height()) {
991 //Resized larger in the vertical direction
992 if(deltaH > 0) {
994 transform->setTranslation(0, -deltaTopPixels);
995 transform->setResize(p_XYBoundingRect.width(),
996 p_XYBoundingRect.height());
997 transform->resizeFirst(true);
998
999 enqueueAction(transform);
1000
1001 QPoint bottomRightOfTopSide(p_XYBoundingRect.right(),
1002 p_XYBoundingRect.top() - deltaTopPixels);
1003
1004 QRect topSideToFill(p_XYBoundingRect.topLeft(), bottomRightOfTopSide);
1005
1006
1007 QPoint topLeftOfbottomSide(p_XYBoundingRect.left(),
1008 p_XYBoundingRect.bottom() -
1009 deltaBottomPixels);
1010
1011 QRect bottomSideToFill(topLeftOfbottomSide,
1012 p_XYBoundingRect.bottomRight());
1013
1014 ViewportBufferFill *topFill = createViewportBufferFill(topSideToFill,
1015 false);
1016 enqueueAction(topFill);
1017
1018 ViewportBufferFill *bottomFill =
1019 createViewportBufferFill(bottomSideToFill, false);
1020 enqueueAction(bottomFill);
1021 }
1022 //Resized smaller in the vertical direction
1023 else if(deltaH < 0) {
1025
1026 transform->setTranslation(0, -deltaTopPixels);
1027 transform->setResize(p_XYBoundingRect.width(),
1028 p_oldXYBoundingRect.height());
1029
1030 enqueueAction(transform);
1031 }
1032 }
1033
1035 }
1036
1037
1045 void ViewportBuffer::pan(int deltaX, int deltaY) {
1047
1049 return;
1050 }
1051
1052
1054 //The sample line bounding rect contains the bounds of the
1055 //screen pixels to the cube sample/line bounds. If the cube
1056 //bounds do not change, then we do not need to do anything to
1057 //the buffer.
1058 return;
1059 }
1060
1061 double deltaLeftSamples = p_sampLineBoundingRect[rectLeft] -
1063 int deltaLeftPixels = (int)round(deltaLeftSamples * p_viewport->scale());
1064
1065 double deltaTopLines = p_sampLineBoundingRect[rectTop] -
1067 int deltaTopPixels = (int)round(deltaTopLines * p_viewport->scale());
1068
1069 // Don't try to figure out panning beyond a full screen,
1070 // even though data could very well be preserved.
1071 if(abs(deltaY) >= p_XYBoundingRect.height() ||
1072 abs(deltaX) >= p_XYBoundingRect.width()) {
1073 reinitialize();
1074 return;
1075 }
1076
1077 //Left side of the visible area changed (start sample is different)
1079 //Shifting data to the right
1080 if(deltaX > 0) {
1081 // The buffer is getting bigger
1083 transform->setResize(p_XYBoundingRect.width(),
1084 p_oldXYBoundingRect.height());
1085 transform->setTranslation(-deltaLeftPixels, 0);
1086 transform->resizeFirst(true);
1087
1088 enqueueAction(transform);
1089
1090 QPoint topLeftOfRefill(p_XYBoundingRect.left(),
1091 p_oldXYBoundingRect.top());
1092
1093 QPoint bottomRightOfRefill(p_XYBoundingRect.left() + deltaX,
1094 p_oldXYBoundingRect.bottom());
1095 QRect fillArea(topLeftOfRefill, bottomRightOfRefill);
1096
1097 ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1098 enqueueAction(fill);
1099 }
1100 //Shifting data to the left
1101 else if(deltaX < 0) {
1102 //The buffer is getting smaller - no new data
1104 transform->setTranslation(-deltaLeftPixels, 0);
1105 transform->setResize(p_XYBoundingRect.width(),
1106 p_oldXYBoundingRect.height());
1107 transform->resizeFirst(false);
1108 enqueueAction(transform);
1109
1110 // if new samples on the screen at all, mark it for reading
1113 QPoint topLeftOfRefill(p_XYBoundingRect.right() + deltaX,
1114 p_oldXYBoundingRect.top());
1115 QPoint bottomRightOfRefill(p_XYBoundingRect.right(),
1116 p_oldXYBoundingRect.bottom());
1117
1118 QRect refillArea(topLeftOfRefill, bottomRightOfRefill);
1119
1121 true);
1122 enqueueAction(fill);
1123 }
1124 }
1125 }
1126 // Left side of the visible area is the same (start sample has not changed,
1127 // but end sample may be different)
1128 else {
1130 transform->setResize(p_XYBoundingRect.width(),
1131 p_oldXYBoundingRect.height());
1132 enqueueAction(transform);
1133
1134 if(deltaX < 0) {
1135 QPoint topLeftOfFillArea(p_XYBoundingRect.right() + deltaX,
1136 p_oldXYBoundingRect.top());
1137
1138 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1139 p_oldXYBoundingRect.bottom());
1140
1141 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1142 ViewportBufferFill *fill = createViewportBufferFill(fillArea, true);
1143
1144 enqueueAction(fill);
1145 }
1146 }
1147
1148 //Top side of the visible area changed (start line is different)
1150 //Shifting data down
1151 if(deltaY > 0) {
1153 transform->setTranslation(0, -deltaTopPixels);
1154 transform->setResize(p_XYBoundingRect.width(),
1155 p_XYBoundingRect.height());
1156 transform->resizeFirst(true);
1157 enqueueAction(transform);
1158
1159 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1160 p_XYBoundingRect.top());
1161 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1162 p_XYBoundingRect.top() + deltaY);
1163 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1164 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1165 enqueueAction(fill);
1166 }
1167 //Shifting data up
1168 else if(deltaY < 0) {
1170 transform->setTranslation(0, -deltaTopPixels);
1171 transform->setResize(p_XYBoundingRect.width(),
1172 p_XYBoundingRect.height());
1173 transform->resizeFirst(false);
1174 enqueueAction(transform);
1175
1176 // if new lines on the screen at all, mark it for reading
1179 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1180 p_oldXYBoundingRect.bottom() + deltaY);
1181 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1182 p_XYBoundingRect.bottom());
1183 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1184 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1185 enqueueAction(fill);
1186 }
1187 }
1188 }
1189 // Top side of the visible area is the same (start line has not changed, but
1190 // end line may be different)
1191 else {
1193 transform->setResize(p_XYBoundingRect.width(),
1194 p_XYBoundingRect.height());
1195 enqueueAction(transform);
1196
1197 if(deltaY < 0) {
1198 QPoint topLeftOfFillArea(p_XYBoundingRect.left(),
1199 p_XYBoundingRect.bottom() + deltaY);
1200 QPoint bottomRightOfFillArea(p_XYBoundingRect.right(),
1201 p_XYBoundingRect.bottom());
1202 QRect fillArea(topLeftOfFillArea, bottomRightOfFillArea);
1203 ViewportBufferFill *fill = createViewportBufferFill(fillArea, false);
1204 enqueueAction(fill);
1205 }
1206 }
1207
1209 }
1210
1211
1218 for(int i = 0; i < p_actions->size(); i++) {
1219 if((*p_actions)[i]->getActionType() == ViewportBufferAction::stretch) {
1220 p_actions->removeAt(i);
1221 i --;
1222 }
1223 }
1224
1227 }
1228
1239 if(force) {
1240 p_buffer.clear();
1241 p_bufferInitialized = false;
1242 }
1243 }
1244
1245
1255 QRect rect = p_XYBoundingRect;
1256
1257 if(!rect.height() || !p_buffer.size())
1258 return QRect();
1259
1260 if(rect.height() > (int) p_buffer.size())
1261 rect.setBottom(rect.top() + p_buffer.size() - 1);
1262
1263 if(rect.width() > (int) p_buffer[0].size())
1264 rect.setRight(rect.left() + p_buffer[0].size() - 1);
1265
1266 return rect;
1267 }
1268
1269
1279 if(!p_enabled)
1280 return;
1281
1282 try {
1284 reinitialize();
1285 }
1286 catch (IException &e) {
1287 throw IException(
1288 e, IException::Programmer, "Unable to change scale.", _FILEINFO_);
1289 }
1290 }
1291
1292
1299 void ViewportBuffer::enable(bool enabled) {
1300 bool wasEnabled = p_enabled;
1301
1303
1304 if(!wasEnabled && p_enabled) {
1306 reinitialize();
1307 }
1308 }
1309
1310
1317 if(p_band == band)
1318 return;
1319 p_band = band;
1320
1322
1323 if(!p_enabled)
1324 return;
1325
1326 reinitialize();
1327 }
1328
1329
1338
1339 try {
1340 // If we're in the middle of a process, we got an okay stretch on startup,
1341 // then we can stop what we're doing.
1342 if(working() && p_initialStretchDone) {
1343 // We only need to handle the current action, can ignore others
1344 ViewportBufferAction *curAction = p_actions->head();
1345
1346 // Delete older actions
1347 for(int i = p_actions->size() - 1; i > 0; i--) {
1348 delete(*p_actions)[i];
1349 p_actions->pop_back();
1350 }
1351
1352 // Deal with current action
1353 if(curAction->started()) {
1354 if(curAction->getActionType() == ViewportBufferAction::fill) {
1355 ViewportBufferFill *fill = (ViewportBufferFill *)curAction;
1356
1357 fill->stop();
1358
1359 p_requestedFillArea = fill->getRect()->height() *
1360 fill->getRect()->width();
1361 }
1362 }
1363 else {
1364 delete curAction;
1365 p_actions->clear();
1366 p_requestedFillArea = 0.0;
1367 }
1368 }
1369
1370
1371 p_bufferInitialized = true;
1372
1374 reset->setResize(0, 0);
1375 enqueueAction(reset);
1376
1377 if (p_XYBoundingRect.isValid()) {
1379 transform->setResize(p_XYBoundingRect.width(), p_XYBoundingRect.height());
1380 enqueueAction(transform);
1382 false);
1383 enqueueAction(fill);
1384 }
1385
1387 }
1388 catch (IException &e) {
1390 "Unable to resize and fill buffer.",
1391 _FILEINFO_);
1392 }
1393 }
1394}
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.