Isis 3 Programmer Reference
CubeIoHandler.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "CubeIoHandler.h"
8
9#include <algorithm>
10#include <cmath>
11#include <iomanip>
12
13#include <QDebug>
14#include <QFile>
15#include <QList>
16#include <QListIterator>
17#include <QMapIterator>
18#include <QMutex>
19#include <QPair>
20#include <QRect>
21#include <QTime>
22
23#include "Area3D.h"
24#include "Brick.h"
25#include "CubeCachingAlgorithm.h"
26#include "Displacement.h"
27#include "Distance.h"
28#include "Endian.h"
29#include "EndianSwapper.h"
30#include "IException.h"
31#include "IString.h"
32#include "PixelType.h"
33#include "Preference.h"
34#include "Pvl.h"
35#include "PvlGroup.h"
36#include "PvlObject.h"
37#include "RawCubeChunk.h"
38#include "RegionalCachingAlgorithm.h"
39#include "SpecialPixel.h"
40#include "Statistics.h"
41
42using namespace std;
43
44namespace Isis {
62 const QList<int> *virtualBandList, const Pvl &label, bool alreadyOnDisk) {
63 m_byteSwapper = NULL;
65 m_dataIsOnDiskMap = NULL;
66 m_rawData = NULL;
67 m_virtualBands = NULL;
68 m_nullChunkData = NULL;
70 m_writeCache = NULL;
71 m_ioThreadPool = NULL;
72 m_writeThreadMutex = NULL;
73
74 try {
75 if (!dataFile) {
76 IString msg = "Cannot create a CubeIoHandler with a NULL data file";
77 throw IException(IException::Programmer, msg, _FILEINFO_);
78 }
79
80 m_cachingAlgorithms = new QList<CubeCachingAlgorithm *>;
81
82 PvlGroup &performancePrefs =
83 Preference::Preferences().findGroup("Performance");
84 IString cubeWritePerfOpt = performancePrefs["CubeWriteThread"][0];
85 m_useOptimizedCubeWrite = (cubeWritePerfOpt.DownCase() == "optimized");
86 if ((m_useOptimizedCubeWrite && !alreadyOnDisk) ||
87 cubeWritePerfOpt.DownCase() == "always") {
88 m_ioThreadPool = new QThreadPool;
89 m_ioThreadPool->setMaxThreadCount(1);
90 }
91
94 m_rawData = new QMap<int, RawCubeChunk *>;
95 m_writeCache = new QPair< QMutex *, QList<Buffer *> >;
96 m_writeCache->first = new QMutex;
97 m_writeThreadMutex = new QMutex;
98
100
102
103 m_dataFile = dataFile;
104
105 const PvlObject &core = label.findObject("IsisCube").findObject("Core");
106 const PvlGroup &pixelGroup = core.findGroup("Pixels");
107
108 QString byteOrderStr = pixelGroup.findKeyword("ByteOrder")[0];
110 byteOrderStr.toUpper());
111 m_base = pixelGroup.findKeyword("Base");
112 m_multiplier = pixelGroup.findKeyword("Multiplier");
113 m_pixelType = PixelTypeEnumeration(pixelGroup.findKeyword("Type"));
114
115 // If the byte swapper isn't going to do anything, then get rid of it
116 // because it's quicker to check for a NULL byte swapper member than to
117 // call a swap that won't do anything.
118 if(!m_byteSwapper->willSwap()) {
119 delete m_byteSwapper;
120 m_byteSwapper = NULL;
121 }
122
123 const PvlGroup &dimensions = core.findGroup("Dimensions");
124 m_numSamples = dimensions.findKeyword("Samples");
125 m_numLines = dimensions.findKeyword("Lines");
126 m_numBands = dimensions.findKeyword("Bands");
127
128 m_startByte = (int)core.findKeyword("StartByte") - 1;
129
130 m_samplesInChunk = -1;
131 m_linesInChunk = -1;
132 m_bandsInChunk = -1;
133
134 if(!alreadyOnDisk) {
135 m_dataIsOnDiskMap = new QMap<int, bool>;
136 }
137
138 setVirtualBands(virtualBandList);
139 }
140 catch(IException &e) {
141 IString msg = "Constructing CubeIoHandler failed";
142 throw IException(e, IException::Programmer, msg, _FILEINFO_);
143 }
144 catch(...) {
145 IString msg = "Constructing CubeIoHandler failed";
146 throw IException(IException::Programmer, msg, _FILEINFO_);
147 }
148 }
149
150
156
157 if (m_ioThreadPool)
158 m_ioThreadPool->waitForDone();
159
160 delete m_ioThreadPool;
161 m_ioThreadPool = NULL;
162
163 delete m_dataIsOnDiskMap;
164 m_dataIsOnDiskMap = NULL;
165
167 QListIterator<CubeCachingAlgorithm *> it(*m_cachingAlgorithms);
168 while (it.hasNext()) {
169 delete it.next();
170 }
171 delete m_cachingAlgorithms;
172 m_cachingAlgorithms = NULL;
173 }
174
175 if (m_rawData) {
176 QMapIterator<int, RawCubeChunk *> it(*m_rawData);
177 while (it.hasNext()) {
178 // Unwritten data here means it cannot be written :(
179 it.next();
180
181 if(it.value())
182 delete it.value();
183 }
184 delete m_rawData;
185 m_rawData = NULL;
186 }
187
188 if (m_writeCache) {
189 delete m_writeCache->first;
190 m_writeCache->first = NULL;
191
192 for (int i = 0; i < m_writeCache->second.size(); i++) {
193 delete m_writeCache->second[i];
194 }
195 m_writeCache->second.clear();
196
197 delete m_writeCache;
198 m_writeCache = NULL;
199 }
200
201 delete m_byteSwapper;
202 m_byteSwapper = NULL;
203
204 delete m_virtualBands;
205 m_virtualBands = NULL;
206
207 delete m_nullChunkData;
208 m_nullChunkData = NULL;
209
212
213 delete m_writeThreadMutex;
214 m_writeThreadMutex = NULL;
215 }
216
217
226 void CubeIoHandler::read(Buffer &bufferToFill) const {
227 // We need to record the current chunk count size so we can use
228 // it to evaluate if the cache should be minimized
229 int lastChunkCount = m_rawData->size();
230
232 // Do the remaining writes
233 flushWriteCache(true);
234
236
237 // Stop backgrounding writes now, we don't want to keep incurring this
238 // penalty.
240 delete m_ioThreadPool;
241 m_ioThreadPool = NULL;
242 }
243 }
244
245 QMutexLocker lock(m_writeThreadMutex);
246
247 // NON-THREADED CUBE READ
248 QList<RawCubeChunk *> cubeChunks;
249 QList<int > chunkBands;
250
251 int bufferSampleCount = bufferToFill.SampleDimension();
252 int bufferLineCount = bufferToFill.LineDimension();
253 int bufferBandCount = bufferToFill.BandDimension();
254
255 // our chunk dimensions are same as buffer shape dimensions
256 if (bufferSampleCount == m_samplesInChunk &&
257 bufferLineCount == m_linesInChunk &&
258 bufferBandCount == m_bandsInChunk) {
259 int bufferStartSample = bufferToFill.Sample();
260 int bufferStartLine = bufferToFill.Line();
261 int bufferStartBand = bufferToFill.Band();
262
263 int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
264 int bufferEndLine = bufferStartLine + bufferLineCount - 1;
265 int bufferEndBand = bufferStartBand + bufferBandCount - 1;
266
267 // make sure we access the correct band
268 int startBand = bufferStartBand - 1;
269 if (m_virtualBands)
270 startBand = m_virtualBands->at(bufferStartBand - 1);
271
272 int expectedChunkIndex =
273 ((bufferStartSample - 1) / getSampleCountInChunk()) +
274 ((bufferStartLine - 1) / getLineCountInChunk()) *
276 ((startBand - 1) / getBandCountInChunk()) *
279
280 int chunkStartSample, chunkStartLine, chunkStartBand,
281 chunkEndSample, chunkEndLine, chunkEndBand;
282
283 getChunkPlacement(expectedChunkIndex,
284 chunkStartSample, chunkStartLine, chunkStartBand,
285 chunkEndSample, chunkEndLine, chunkEndBand);
286
287 if (chunkStartSample == bufferStartSample &&
288 chunkStartLine == bufferStartLine &&
289 chunkStartBand == bufferStartBand &&
290 chunkEndSample == bufferEndSample &&
291 chunkEndLine == bufferEndLine &&
292 chunkEndBand == bufferEndBand) {
293 cubeChunks.append(getChunk(expectedChunkIndex, true));
294 chunkBands.append(cubeChunks.last()->getStartBand());
295 }
296 }
297
298 if (cubeChunks.empty()) {
299 // We can't guarantee our cube chunks will encompass the buffer
300 // if the buffer goes beyond the cube bounds.
301 for(int i = 0; i < bufferToFill.size(); i++) {
302 bufferToFill[i] = Null;
303 }
304
305 QPair< QList<RawCubeChunk *>, QList<int> > chunkInfo;
306 chunkInfo = findCubeChunks(
307 bufferToFill.Sample(), bufferToFill.SampleDimension(),
308 bufferToFill.Line(), bufferToFill.LineDimension(),
309 bufferToFill.Band(), bufferToFill.BandDimension());
310 cubeChunks = chunkInfo.first;
311 chunkBands = chunkInfo.second;
312 }
313
314 for (int i = 0; i < cubeChunks.size(); i++) {
315 writeIntoDouble(*cubeChunks[i], bufferToFill, chunkBands[i]);
316 }
317
318 // Minimize the cache if it changed in size
319 if (lastChunkCount != m_rawData->size()) {
320 minimizeCache(cubeChunks, bufferToFill);
321 }
322 }
323
324
334 void CubeIoHandler::write(const Buffer &bufferToWrite) {
336
337 if (m_ioThreadPool) {
338 // THREADED CUBE WRITE
339 Buffer * copy = new Buffer(bufferToWrite);
340 {
341 QMutexLocker locker(m_writeCache->first);
342 m_writeCache->second.append(copy);
343 }
344
346 }
347 else {
348 QMutexLocker lock(m_writeThreadMutex);
349 // NON-THREADED CUBE WRITE
350 synchronousWrite(bufferToWrite);
351 }
352 }
353
354
365 m_cachingAlgorithms->prepend(algorithm);
366 }
367
368
379 void CubeIoHandler::clearCache(bool blockForWriteCache) const {
380 if (blockForWriteCache) {
381 // Start the rest of the writes
382 flushWriteCache(true);
383 }
384
385 // If this map is allocated, then this is a brand new cube and we need to
386 // make sure it's filled with data or NULLs.
389 }
390
391 // This should be allocated. This is a list of the cached cube data.
392 // Write it all to disk.
393 if (m_rawData) {
394 QMapIterator<int, RawCubeChunk *> it(*m_rawData);
395 while (it.hasNext()) {
396 it.next();
397
398 if(it.value()) {
399 if(it.value()->isDirty()) {
400 (const_cast<CubeIoHandler *>(this))->writeRaw(*it.value());
401 }
402
403 delete it.value();
404 }
405 }
406
407 m_rawData->clear();
408 }
409
413 }
414 }
415
416
427
428
438 void CubeIoHandler::setVirtualBands(const QList<int> *virtualBandList) {
439 if(m_virtualBands) {
440 delete m_virtualBands;
441 m_virtualBands = NULL;
442 }
443
444 if(virtualBandList && !virtualBandList->empty())
445 m_virtualBands = new QList<int>(*virtualBandList);
446 }
447
458
463 return m_numBands;
464 }
465
466
471 return m_bandsInChunk;
472 }
473
474
486
487
493 return (int)ceil((double)m_numBands / (double)m_bandsInChunk);
494 }
495
496
502 return (int)ceil((double)m_numLines / (double)m_linesInChunk);
503 }
504
505
511 return (int)ceil((double)m_numSamples / (double)m_samplesInChunk);
512 }
513
514
527
528 int sampleIndex = (chunk.getStartSample() - 1) / getSampleCountInChunk();
529 int lineIndex = (chunk.getStartLine() - 1) / getLineCountInChunk();
530 int bandIndex = (chunk.getStartBand() - 1) / getBandCountInChunk();
531
532 int indexInBand =
533 sampleIndex + lineIndex * getChunkCountInSampleDimension();
534 int indexOffsetToBand = bandIndex * getChunkCountInSampleDimension() *
536
537 return indexOffsetToBand + indexInBand;
538 }
539
540
547
548
556 return m_dataFile;
557 }
558
559
565 return m_numLines;
566 }
567
568
573 return m_linesInChunk;
574 }
575
576
581 return m_pixelType;
582 }
583
584
590 return m_numSamples;
591 }
592
593
600
601
615 int numSamples, int numLines, int numBands) {
616 bool success = false;
617 IString msg;
618
619 if(m_samplesInChunk != -1 || m_linesInChunk != -1 || m_bandsInChunk != -1) {
620 IString msg = "You cannot change the chunk sizes once set";
621 }
622 else if(numSamples < 1) {
623 msg = "Negative and zero chunk sizes are not supported, samples per chunk"
624 " cannot be [" + IString(numSamples) + "]";
625 }
626 else if(numLines < 1) {
627 msg = "Negative and zero chunk sizes are not supported, lines per chunk "
628 "cannot be [" + IString(numLines) + "]";
629 }
630 else if(numBands < 1) {
631 msg = "Negative and zero chunk sizes are not supported, lines per chunk "
632 "cannot be [" + IString(numBands) + "]";
633 }
634 else {
635 success = true;
636 }
637
638 if(success) {
639 m_samplesInChunk = numSamples;
640 m_linesInChunk = numLines;
641 m_bandsInChunk = numBands;
642
645 }
646 else if(m_dataFile->size() < getDataStartByte() + getDataSize()) {
647 success = false;
648 msg = "File size [" + IString((BigInt)m_dataFile->size()) +
649 " bytes] not big enough to hold data [" +
650 IString(getDataStartByte() + getDataSize()) + " bytes] where the "
651 "offset to the cube data is [" + IString(getDataStartByte()) +
652 " bytes]";
653 }
654 }
655 else {
656 throw IException(IException::Programmer, msg, _FILEINFO_);
657 }
658 }
659
660
668 if (m_ioThreadPool) {
669 QMutexLocker lock(m_writeThreadMutex);
670 }
671 }
672
673
681 bool CubeIoHandler::bufferLessThan(Buffer * const &lhs, Buffer * const &rhs) {
682 bool lessThan = false;
683
684 // If there is any overlap then we need to return false due to it being
685 // ambiguous.
686 Area3D lhsArea(
687 Displacement(lhs->Sample(), Displacement::Pixels),
690 Distance(lhs->SampleDimension() - 1, Distance::Pixels),
691 Distance(lhs->LineDimension() - 1, Distance::Pixels),
692 Distance(lhs->BandDimension() - 1, Distance::Pixels));
693 Area3D rhsArea(
694 Displacement(rhs->Sample(), Displacement::Pixels),
697 Distance(rhs->SampleDimension() - 1, Distance::Pixels),
698 Distance(rhs->LineDimension() - 1, Distance::Pixels),
699 Distance(rhs->BandDimension() - 1, Distance::Pixels));
700
701 if (!lhsArea.intersect(rhsArea).isValid()) {
702 if (lhs->Band() != rhs->Band()) {
703 lessThan = lhs->Band() < rhs->Band();
704 }
705 else if (lhs->Line() != rhs->Line()) {
706 lessThan = lhs->Line() < rhs->Line();
707 }
708 else if (lhs->Sample() != rhs->Sample()) {
709 lessThan = lhs->Sample() < rhs->Sample();
710 }
711 }
712
713 return lessThan;
714 }
715
716
730 QPair< QList<RawCubeChunk *>, QList<int> > CubeIoHandler::findCubeChunks(int startSample,
731 int numSamples, int startLine, int numLines, int startBand,
732 int numBands) const {
733 QList<RawCubeChunk *> results;
734 QList<int> resultBands;
735/************************************************************************CHANGED THIS!!!!!!!!******/
736 int lastBand = startBand + numBands - 1;
737// int lastBand = min(startBand + numBands - 1,
738// bandCount());
739/************************************************************************CHANGED THIS!!!!!!!!******/
740
741 QRect areaInBand(
742 QPoint(max(startSample, 1),
743 max(startLine, 1)),
744 QPoint(min(startSample + numSamples - 1,
745 sampleCount()),
746 min(startLine + numLines - 1,
747 lineCount())));
748
749 // We are considering only 1 band at a time.. we can't use m_bandsInChunk
750 // because of virtual bands, but every extra loop should not need extra
751 // IO.
752 for(int band = startBand; band <= lastBand; band ++) {
753 // This is the user-requested area in this band
754 QRect areaLeftInBand(areaInBand);
755
756 int actualBand = band;
757
758 if(m_virtualBands) {
759 if (band < 1 || band > m_virtualBands->size())
760 actualBand = 0;
761 else
762 actualBand = (m_virtualBands->at(band - 1) - 1) / m_bandsInChunk + 1;
763 }
764 // We will be consuming areaLeftInBand until we've got all of the area
765 // requested.
766 while(!areaLeftInBand.isEmpty()) {
814 int areaStartLine = areaLeftInBand.top();
815 int areaStartSample = areaLeftInBand.left();
816
817 int initialChunkXPos = (areaStartSample - 1) / m_samplesInChunk;
818 int initialChunkYPos = (areaStartLine - 1) / m_linesInChunk;
819 int initialChunkZPos = (actualBand - 1) / m_bandsInChunk;
820 int initialChunkBand = initialChunkZPos * m_bandsInChunk + 1;
821
822
823 QRect chunkRect(initialChunkXPos * m_samplesInChunk + 1,
824 initialChunkYPos * m_linesInChunk + 1,
826
827 // The chunk rectangle must intersect the remaining area that is in the
828 // current band, and the chunk's initial band must be between 1 and the
829 // number of physical bands in the cube (inclusive).
830 while(chunkRect.intersects(areaLeftInBand) &&
831 (initialChunkBand >= 1 && initialChunkBand <= bandCount())) {
832 int chunkXPos = (chunkRect.left() - 1) / m_samplesInChunk;
833 int chunkYPos = (chunkRect.top() - 1) / m_linesInChunk;
834 int chunkZPos = initialChunkZPos;
835
836 // We now have an X,Y,Z position for the chunk. What's its index?
837 int chunkIndex = chunkXPos +
838 (chunkYPos * getChunkCountInSampleDimension()) +
839 (chunkZPos * getChunkCountInSampleDimension() *
841
842 RawCubeChunk * newChunk = getChunk(chunkIndex, true);
843
844 results.append(newChunk);
845 resultBands.append(band);
846
847 chunkRect.moveLeft(chunkRect.right() + 1);
848 }
849
850 areaLeftInBand.setTop(chunkRect.bottom() + 1);
851 }
852 }
853
854 return QPair< QList<RawCubeChunk *>, QList<int> >(results, resultBands);
855 }
856
857
876 const RawCubeChunk &cube1, const Buffer &cube2,
877 int &startX, int &startY, int &startZ,
878 int &endX, int &endY, int &endZ) const {
879 // So we have 2 3D "cubes" (not Cube cubes but 3d areas) we need to
880 // intersect in order to figure out what chunk data goes into the output
881 // buffer.
882
883 // To find the band range we actually have to map all of the bands from
884 // virtual bands to physical bands
885 int startVBand = cube2.Band();
886 int endVBand = startVBand + cube2.BandDimension() - 1;
887
888 int startPhysicalBand = 0;
889 int endPhysicalBand = 0;
890
891 bool startVBandFound = false;
892 for(int virtualBand = startVBand; virtualBand <= endVBand; virtualBand ++) {
893 int physicalBand = virtualBand;
894
895 bool bandExists = true;
896 if(m_virtualBands) {
897 if (virtualBand < 1 || virtualBand > m_virtualBands->size())
898 bandExists = false;
899 else {
900 physicalBand = m_virtualBands->at(virtualBand - 1);
901 }
902 }
903
904 if (bandExists) {
905 if(!startVBandFound) {
906 startPhysicalBand = physicalBand;
907 endPhysicalBand = physicalBand;
908 startVBandFound = true;
909 }
910 else {
911 if(physicalBand < startPhysicalBand)
912 startPhysicalBand = physicalBand;
913
914 if(physicalBand > endPhysicalBand)
915 endPhysicalBand = physicalBand;
916 }
917 }
918 }
919
920 startX = max(cube1.getStartSample(), cube2.Sample());
921 startY = max(cube1.getStartLine(), cube2.Line());
922 startZ = max(cube1.getStartBand(), startPhysicalBand);
923 endX = min(cube1.getStartSample() + cube1.sampleCount() - 1,
924 cube2.Sample() + cube2.SampleDimension() - 1);
925 endY = min(cube1.getStartLine() + cube1.lineCount() - 1,
926 cube2.Line() + cube2.LineDimension() - 1);
927 endZ = min(cube1.getStartBand() + cube1.bandCount() - 1,
928 endPhysicalBand);
929 }
930
931
940 void CubeIoHandler::flushWriteCache(bool force) const {
941 if (m_ioThreadPool) {
942 bool shouldFlush = m_writeCache->second.size() >= m_idealFlushSize ||
943 force;
944 bool cacheOverflowing =
945 (m_writeCache->second.size() > m_idealFlushSize * 10);
946 bool shouldAndCanFlush = false;
947 bool forceStart = force;
948
949 if (shouldFlush) {
950 shouldAndCanFlush = m_writeThreadMutex->tryLock();
951 if (shouldAndCanFlush) {
952 m_writeThreadMutex->unlock();
953 }
954 }
955
956 if (cacheOverflowing && !shouldAndCanFlush) {
957 forceStart = true;
959 }
960
961 if (forceStart) {
963
964 if (m_writeCache->second.size() != 0) {
965 m_idealFlushSize = m_writeCache->second.size();
966 shouldFlush = true;
967 shouldAndCanFlush = true;
968 }
969 }
970 else if (!cacheOverflowing && shouldAndCanFlush) {
972 }
973
974 if (cacheOverflowing && m_useOptimizedCubeWrite) {
976
977 // If the process is very I/O bound, then write caching isn't helping
978 // anything. In fact, it hurts, so turn it off.
980 delete m_ioThreadPool;
981 m_ioThreadPool = NULL;
982 }
983
984 // Write it all synchronously.
985 foreach (Buffer *bufferToWrite, m_writeCache->second) {
986 const_cast<CubeIoHandler *>(this)->synchronousWrite(*bufferToWrite);
987 delete bufferToWrite;
988 }
989
990 m_writeCache->second.clear();
991 }
992
993 if (shouldAndCanFlush && m_ioThreadPool) {
994 QMutexLocker locker(m_writeCache->first);
995 QRunnable *writer = new BufferToChunkWriter(
996 const_cast<CubeIoHandler *>(this), m_writeCache->second);
997
998 m_ioThreadPool->start(writer);
999
1000 m_writeCache->second.clear();
1002 }
1003
1004 if (force) {
1006 }
1007 }
1008 }
1009
1010
1017 void CubeIoHandler::freeChunk(RawCubeChunk *chunkToFree) const {
1018 if(chunkToFree && m_rawData) {
1019 int chunkIndex = getChunkIndex(*chunkToFree);
1020
1021 m_rawData->erase(m_rawData->find(chunkIndex));
1022
1023 if(chunkToFree->isDirty())
1024 (const_cast<CubeIoHandler *>(this))->writeRaw(*chunkToFree);
1025
1026 delete chunkToFree;
1027
1031 }
1032 }
1033 }
1034
1035
1046 bool allocateIfNecessary) const {
1047 RawCubeChunk *chunk = NULL;
1048
1049 if(m_rawData) {
1050 chunk = m_rawData->value(chunkIndex);
1051 }
1052
1053 if(allocateIfNecessary && !chunk) {
1054 if(m_dataIsOnDiskMap && !(*m_dataIsOnDiskMap)[chunkIndex]) {
1055 chunk = getNullChunk(chunkIndex);
1056 (*m_dataIsOnDiskMap)[chunkIndex] = true;
1057 }
1058 else {
1059 int startSample;
1060 int startLine;
1061 int startBand;
1062 int endSample;
1063 int endLine;
1064 int endBand;
1065 getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1066 endSample, endLine, endBand);
1067 chunk = new RawCubeChunk(startSample, startLine, startBand,
1068 endSample, endLine, endBand,
1070
1071 (const_cast<CubeIoHandler *>(this))->readRaw(*chunk);
1072 chunk->setDirty(false);
1073 }
1074
1075 (*m_rawData)[chunkIndex] = chunk;
1076 }
1077
1078 return chunk;
1079 }
1080
1081
1091
1092
1105 int &startSample, int &startLine, int &startBand,
1106 int &endSample, int &endLine, int &endBand) const {
1107 int chunkSampleIndex = chunkIndex % getChunkCountInSampleDimension();
1108
1109 chunkIndex =
1110 (chunkIndex - chunkSampleIndex) / getChunkCountInSampleDimension();
1111
1112 int chunkLineIndex = chunkIndex % getChunkCountInLineDimension();
1113 chunkIndex = (chunkIndex - chunkLineIndex) / getChunkCountInLineDimension();
1114
1115 int chunkBandIndex = chunkIndex;
1116
1117 startSample = chunkSampleIndex * getSampleCountInChunk() + 1;
1118 endSample = startSample + getSampleCountInChunk() - 1;
1119 startLine = chunkLineIndex * getLineCountInChunk() + 1;
1120 endLine = startLine + getLineCountInChunk() - 1;
1121 startBand = chunkBandIndex * getBandCountInChunk() + 1;
1122 endBand = startBand + getBandCountInChunk() - 1;
1123 }
1124
1125
1137 // Shouldn't ask for null chunks when the area has already been allocated
1138
1139 int startSample = 0;
1140 int startLine = 0;
1141 int startBand = 0;
1142
1143 int endSample = 0;
1144 int endLine = 0;
1145 int endBand = 0;
1146
1147 getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1148 endSample, endLine, endBand);
1149
1150 RawCubeChunk *result = new RawCubeChunk(startSample, startLine, startBand,
1151 endSample, endLine, endBand, getBytesPerChunk());
1152
1153 if(!m_nullChunkData) {
1154 // the pixel type doesn't really matter, so pick something small
1155 Brick nullBuffer(result->sampleCount(),
1156 result->lineCount(),
1157 result->bandCount(),
1158 UnsignedByte);
1159
1160 nullBuffer.SetBasePosition(result->getStartSample(),
1161 result->getStartLine(),
1162 result->getStartBand());
1163 for(int i = 0; i < nullBuffer.size(); i++) {
1164 nullBuffer[i] = Null;
1165 }
1166
1167 writeIntoRaw(nullBuffer, *result, result->getStartBand());
1168 m_nullChunkData = new QByteArray(result->getRawData());
1169 }
1170 else {
1171 result->setRawData(*m_nullChunkData);
1172 }
1173
1174 result->setDirty(true);
1175 return result;
1176 }
1177
1178
1188 void CubeIoHandler::minimizeCache(const QList<RawCubeChunk *> &justUsed,
1189 const Buffer &justRequested) const {
1190 // Since we have a lock on the cache, no newly created threads can utilize
1191 // or access any cache data until we're done.
1192 if (m_rawData->size() * getBytesPerChunk() > 1 * 1024 * 1024 ||
1193 m_cachingAlgorithms->size() > 1) {
1194 bool algorithmAccepted = false;
1195
1196 int algorithmIndex = 0;
1197 while(!algorithmAccepted &&
1198 algorithmIndex < m_cachingAlgorithms->size()) {
1199 CubeCachingAlgorithm *algorithm = (*m_cachingAlgorithms)[algorithmIndex];
1200
1202 algorithm->recommendChunksToFree(m_rawData->values(), justUsed,
1203 justRequested);
1204
1205 algorithmAccepted = result.algorithmUnderstoodData();
1206
1207 if(algorithmAccepted) {
1208 QList<RawCubeChunk *> chunksToFree = result.getChunksToFree();
1209
1210 RawCubeChunk *chunkToFree;
1211 foreach(chunkToFree, chunksToFree) {
1212 freeChunk(chunkToFree);
1213 }
1214 }
1215
1216 algorithmIndex ++;
1217 }
1218
1219 // Fall back - no algorithms liked us :(
1220 if(!algorithmAccepted && m_rawData->size() > 100) {
1221 // This (minimizeCache()) is typically executed in the Runnable thread.
1222 // We don't want to wait for ourselves.
1223 clearCache(false);
1224 }
1225 }
1226 }
1227
1228
1237 void CubeIoHandler::synchronousWrite(const Buffer &bufferToWrite) {
1238 QList<RawCubeChunk *> cubeChunks;
1239 QList<int> cubeChunkBands;
1240
1241 int bufferSampleCount = bufferToWrite.SampleDimension();
1242 int bufferLineCount = bufferToWrite.LineDimension();
1243 int bufferBandCount = bufferToWrite.BandDimension();
1244
1245 // process by line optimization
1247 m_lastProcessByLineChunks->size()) {
1248 // Not optimized yet, let's see if we can optimize
1249 if(bufferToWrite.Sample() == 1 &&
1250 bufferSampleCount == sampleCount() &&
1251 bufferLineCount == 1 &&
1252 bufferBandCount == 1) {
1253 // We look like a process by line, are we using the same chunks as
1254 // before?
1255 int bufferLine = bufferToWrite.Line();
1256 int chunkStartLine =
1257 (*m_lastProcessByLineChunks)[0]->getStartLine();
1258 int chunkLines =
1259 (*m_lastProcessByLineChunks)[0]->lineCount();
1260 int bufferBand = bufferToWrite.Band();
1261 int chunkStartBand =
1262 (*m_lastProcessByLineChunks)[0]->getStartBand();
1263 int chunkBands =
1264 (*m_lastProcessByLineChunks)[0]->bandCount();
1265
1266 if(bufferLine >= chunkStartLine &&
1267 bufferLine <= chunkStartLine + chunkLines - 1 &&
1268 bufferBand >= chunkStartBand &&
1269 bufferBand <= chunkStartBand + chunkBands - 1) {
1270 cubeChunks = *m_lastProcessByLineChunks;
1271 for (int i = 0; i < cubeChunks.size(); i++) {
1272 cubeChunkBands.append( cubeChunks[i]->getStartBand() );
1273 }
1274 }
1275 }
1276 }
1277 // Processing by chunk size
1278 else if (bufferSampleCount == m_samplesInChunk &&
1279 bufferLineCount == m_linesInChunk &&
1280 bufferBandCount == m_bandsInChunk) {
1281 int bufferStartSample = bufferToWrite.Sample();
1282 int bufferStartLine = bufferToWrite.Line();
1283 int bufferStartBand = bufferToWrite.Band();
1284
1285 int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
1286 int bufferEndLine = bufferStartLine + bufferLineCount - 1;
1287 int bufferEndBand = bufferStartBand + bufferBandCount - 1;
1288
1289 int expectedChunkIndex =
1290 ((bufferStartSample - 1) / getSampleCountInChunk()) +
1291 ((bufferStartLine - 1) / getLineCountInChunk()) *
1293 ((bufferStartBand - 1) / getBandCountInChunk()) *
1296
1297 int chunkStartSample, chunkStartLine, chunkStartBand,
1298 chunkEndSample, chunkEndLine, chunkEndBand;
1299
1300 getChunkPlacement(expectedChunkIndex,
1301 chunkStartSample, chunkStartLine, chunkStartBand,
1302 chunkEndSample, chunkEndLine, chunkEndBand);
1303
1304 if (chunkStartSample == bufferStartSample &&
1305 chunkStartLine == bufferStartLine &&
1306 chunkStartBand == bufferStartBand &&
1307 chunkEndSample == bufferEndSample &&
1308 chunkEndLine == bufferEndLine &&
1309 chunkEndBand == bufferEndBand) {
1310 cubeChunks.append(getChunk(expectedChunkIndex, true));
1311 cubeChunkBands.append(cubeChunks.last()->getStartBand());
1312 }
1313 }
1314
1315 QPair< QList<RawCubeChunk *>, QList<int> > chunkInfo;
1316 if(cubeChunks.empty()) {
1317 chunkInfo = findCubeChunks(
1318 bufferToWrite.Sample(), bufferSampleCount,
1319 bufferToWrite.Line(), bufferLineCount,
1320 bufferToWrite.Band(), bufferBandCount);
1321 cubeChunks = chunkInfo.first;
1322 cubeChunkBands = chunkInfo.second;
1323 }
1324
1325 // process by line optimization
1326 if(bufferToWrite.Sample() == 1 &&
1327 bufferSampleCount == sampleCount() &&
1328 bufferLineCount == 1 &&
1329 bufferBandCount == 1) {
1332 new QList<RawCubeChunk *>(cubeChunks);
1333 else
1334 *m_lastProcessByLineChunks = cubeChunks;
1335 }
1336
1337 for(int i = 0; i < cubeChunks.size(); i++) {
1338 writeIntoRaw(bufferToWrite, *cubeChunks[i], cubeChunkBands[i]);
1339 }
1340
1341 minimizeCache(cubeChunks, bufferToWrite);
1342 }
1343
1344
1353 Buffer &output, int index) const {
1354 // The code in this method is highly optimized. Even the order of the if
1355 // statements will have a significant impact on performance if changed.
1356 // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1357 // writeIntoRaw(...). This is needed for performance gain. Any function
1358 // or method calls from within the x loop cause significant performance
1359 // decreases.
1360 int startX = 0;
1361 int startY = 0;
1362 int startZ = 0;
1363
1364 int endX = 0;
1365 int endY = 0;
1366 int endZ = 0;
1367
1368 findIntersection(chunk, output, startX, startY, startZ, endX, endY, endZ);
1369
1370 int bufferBand = output.Band();
1371 int bufferBands = output.BandDimension();
1372 int chunkStartSample = chunk.getStartSample();
1373 int chunkStartLine = chunk.getStartLine();
1374 int chunkStartBand = chunk.getStartBand();
1375 int chunkLineSize = chunk.sampleCount();
1376 int chunkBandSize = chunkLineSize * chunk.lineCount();
1377 //double *buffersDoubleBuf = output.p_buf;
1378 double *buffersDoubleBuf = output.DoubleBuffer();
1379 const char *chunkBuf = chunk.getRawData().data();
1380 char *buffersRawBuf = (char *)output.RawBuffer();
1381
1382 for(int z = startZ; z <= endZ; z++) {
1383 const int &bandIntoChunk = z - chunkStartBand;
1384 int virtualBand = z;
1385
1386 virtualBand = index;
1387
1388 if(virtualBand != 0 && virtualBand >= bufferBand &&
1389 virtualBand <= bufferBand + bufferBands - 1) {
1390
1391 for(int y = startY; y <= endY; y++) {
1392 const int &lineIntoChunk = y - chunkStartLine;
1393 int bufferIndex = output.Index(startX, y, virtualBand);
1394
1395 for(int x = startX; x <= endX; x++) {
1396 const int &sampleIntoChunk = x - chunkStartSample;
1397
1398 const int &chunkIndex = sampleIntoChunk +
1399 (chunkLineSize * lineIntoChunk) +
1400 (chunkBandSize * bandIntoChunk);
1401
1402 double &bufferVal = buffersDoubleBuf[bufferIndex];
1403
1404 if(m_pixelType == Real) {
1405 float raw = ((float *)chunkBuf)[chunkIndex];
1406 if(m_byteSwapper)
1407 raw = m_byteSwapper->Float(&raw);
1408
1409 if(raw >= VALID_MIN4) {
1410 bufferVal = (double) raw;
1411 }
1412 else {
1413 if(raw == NULL4)
1414 bufferVal = NULL8;
1415 else if(raw == LOW_INSTR_SAT4)
1416 bufferVal = LOW_INSTR_SAT8;
1417 else if(raw == LOW_REPR_SAT4)
1418 bufferVal = LOW_REPR_SAT8;
1419 else if(raw == HIGH_INSTR_SAT4)
1420 bufferVal = HIGH_INSTR_SAT8;
1421 else if(raw == HIGH_REPR_SAT4)
1422 bufferVal = HIGH_REPR_SAT8;
1423 else
1424 bufferVal = LOW_REPR_SAT8;
1425 }
1426
1427 ((float *)buffersRawBuf)[bufferIndex] = raw;
1428 }
1429
1430 else if(m_pixelType == SignedWord) {
1431 short raw = ((short *)chunkBuf)[chunkIndex];
1432 if(m_byteSwapper)
1433 raw = m_byteSwapper->ShortInt(&raw);
1434
1435 if(raw >= VALID_MIN2) {
1436 bufferVal = (double) raw * m_multiplier + m_base;
1437 }
1438 else {
1439 if(raw == NULL2)
1440 bufferVal = NULL8;
1441 else if(raw == LOW_INSTR_SAT2)
1442 bufferVal = LOW_INSTR_SAT8;
1443 else if(raw == LOW_REPR_SAT2)
1444 bufferVal = LOW_REPR_SAT8;
1445 else if(raw == HIGH_INSTR_SAT2)
1446 bufferVal = HIGH_INSTR_SAT8;
1447 else if(raw == HIGH_REPR_SAT2)
1448 bufferVal = HIGH_REPR_SAT8;
1449 else
1450 bufferVal = LOW_REPR_SAT8;
1451 }
1452
1453 ((short *)buffersRawBuf)[bufferIndex] = raw;
1454 }
1455
1456
1457 else if(m_pixelType == UnsignedWord) {
1458 unsigned short raw = ((unsigned short *)chunkBuf)[chunkIndex];
1459 if(m_byteSwapper)
1460 raw = m_byteSwapper->UnsignedShortInt(&raw);
1461
1462 if(raw >= VALID_MINU2) {
1463 bufferVal = (double) raw * m_multiplier + m_base;
1464 }
1465 else if (raw > VALID_MAXU2) {
1466 if(raw == HIGH_INSTR_SATU2)
1467 bufferVal = HIGH_INSTR_SAT8;
1468 else if(raw == HIGH_REPR_SATU2)
1469 bufferVal = HIGH_REPR_SAT8;
1470 else
1471 bufferVal = LOW_REPR_SAT8;
1472 }
1473 else {
1474 if(raw == NULLU2)
1475 bufferVal = NULL8;
1476 else if(raw == LOW_INSTR_SATU2)
1477 bufferVal = LOW_INSTR_SAT8;
1478 else if(raw == LOW_REPR_SATU2)
1479 bufferVal = LOW_REPR_SAT8;
1480 else
1481 bufferVal = LOW_REPR_SAT8;
1482 }
1483
1484 ((unsigned short *)buffersRawBuf)[bufferIndex] = raw;
1485 }
1486
1487 else if(m_pixelType == UnsignedInteger) {
1488
1489 unsigned int raw = ((unsigned int *)chunkBuf)[chunkIndex];
1490 if(m_byteSwapper)
1491 raw = m_byteSwapper->Uint32_t(&raw);
1492
1493 if(raw >= VALID_MINUI4) {
1494 bufferVal = (double) raw * m_multiplier + m_base;
1495 }
1496 else if (raw > VALID_MAXUI4) {
1497 if(raw == HIGH_INSTR_SATUI4)
1498 bufferVal = HIGH_INSTR_SAT8;
1499 else if(raw == HIGH_REPR_SATUI4)
1500 bufferVal = HIGH_REPR_SAT8;
1501 else
1502 bufferVal = LOW_REPR_SAT8;
1503 }
1504 else {
1505 if(raw == NULLUI4)
1506 bufferVal = NULL8;
1507 else if(raw == LOW_INSTR_SATUI4)
1508 bufferVal = LOW_INSTR_SAT8;
1509 else if(raw == LOW_REPR_SATUI4)
1510 bufferVal = LOW_REPR_SAT8;
1511 else
1512 bufferVal = LOW_REPR_SAT8;
1513 }
1514
1515 ((unsigned int *)buffersRawBuf)[bufferIndex] = raw;
1516
1517
1518
1519 }
1520
1521 else if(m_pixelType == UnsignedByte) {
1522 unsigned char raw = ((unsigned char *)chunkBuf)[chunkIndex];
1523
1524 if(raw == NULL1) {
1525 bufferVal = NULL8;
1526 }
1527 else if(raw == HIGH_REPR_SAT1) {
1528 bufferVal = HIGH_REPR_SAT8;
1529 }
1530 else {
1531 bufferVal = (double) raw * m_multiplier + m_base;
1532 }
1533
1534 ((unsigned char *)buffersRawBuf)[bufferIndex] = raw;
1535 }
1536
1537 bufferIndex ++;
1538 }
1539 }
1540 }
1541 }
1542 }
1543
1544
1552 void CubeIoHandler::writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index)
1553 const {
1554 // The code in this method is highly optimized. Even the order of the if
1555 // statements will have a significant impact on performance if changed.
1556 // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1557 // writeIntoRaw(...). This is needed for performance gain. Any function
1558 // or method calls from within the x loop cause significant performance
1559 // decreases.
1560 int startX = 0;
1561 int startY = 0;
1562 int startZ = 0;
1563
1564 int endX = 0;
1565 int endY = 0;
1566 int endZ = 0;
1567
1568 output.setDirty(true);
1569 findIntersection(output, buffer, startX, startY, startZ, endX, endY, endZ);
1570
1571 int bufferBand = buffer.Band();
1572 int bufferBands = buffer.BandDimension();
1573 int outputStartSample = output.getStartSample();
1574 int outputStartLine = output.getStartLine();
1575 int outputStartBand = output.getStartBand();
1576 int lineSize = output.sampleCount();
1577 int bandSize = lineSize * output.lineCount();
1578 double *buffersDoubleBuf = buffer.DoubleBuffer();
1579 char *chunkBuf = output.getRawData().data();
1580
1581 for(int z = startZ; z <= endZ; z++) {
1582 const int &bandIntoChunk = z - outputStartBand;
1583 int virtualBand = index;
1584
1585
1586 if(m_virtualBands) {
1587 virtualBand = m_virtualBands->indexOf(virtualBand) + 1;
1588 }
1589
1590 if(virtualBand != 0 && virtualBand >= bufferBand &&
1591 virtualBand <= bufferBand + bufferBands - 1) {
1592
1593 for(int y = startY; y <= endY; y++) {
1594 const int &lineIntoChunk = y - outputStartLine;
1595 int bufferIndex = buffer.Index(startX, y, virtualBand);
1596
1597 for(int x = startX; x <= endX; x++) {
1598 const int &sampleIntoChunk = x - outputStartSample;
1599
1600 const int &chunkIndex = sampleIntoChunk +
1601 (lineSize * lineIntoChunk) + (bandSize * bandIntoChunk);
1602
1603 double bufferVal = buffersDoubleBuf[bufferIndex];
1604
1605 if(m_pixelType == Real) {
1606 float raw = 0;
1607
1608 if(bufferVal >= VALID_MIN8) {
1609 double filePixelValueDbl = (bufferVal - m_base) /
1611
1612 if(filePixelValueDbl < (double) VALID_MIN4) {
1613 raw = LOW_REPR_SAT4;
1614 }
1615 else if(filePixelValueDbl > (double) VALID_MAX4) {
1616 raw = HIGH_REPR_SAT4;
1617 }
1618 else {
1619 raw = (float) filePixelValueDbl;
1620 }
1621 }
1622 else {
1623 if(bufferVal == NULL8)
1624 raw = NULL4;
1625 else if(bufferVal == LOW_INSTR_SAT8)
1626 raw = LOW_INSTR_SAT4;
1627 else if(bufferVal == LOW_REPR_SAT8)
1628 raw = LOW_REPR_SAT4;
1629 else if(bufferVal == HIGH_INSTR_SAT8)
1630 raw = HIGH_INSTR_SAT4;
1631 else if(bufferVal == HIGH_REPR_SAT8)
1632 raw = HIGH_REPR_SAT4;
1633 else
1634 raw = LOW_REPR_SAT4;
1635 }
1636 ((float *)chunkBuf)[chunkIndex] =
1637 m_byteSwapper ? m_byteSwapper->Float(&raw) : raw;
1638 }
1639
1640 else if(m_pixelType == SignedWord) {
1641 short raw;
1642
1643 if(bufferVal >= VALID_MIN8) {
1644 double filePixelValueDbl = (bufferVal - m_base) /
1646 if(filePixelValueDbl < VALID_MIN2 - 0.5) {
1647 raw = LOW_REPR_SAT2;
1648 }
1649 if(filePixelValueDbl > VALID_MAX2 + 0.5) {
1650 raw = HIGH_REPR_SAT2;
1651 }
1652 else {
1653 int filePixelValue = (int)round(filePixelValueDbl);
1654
1655 if(filePixelValue < VALID_MIN2) {
1656 raw = LOW_REPR_SAT2;
1657 }
1658 else if(filePixelValue > VALID_MAX2) {
1659 raw = HIGH_REPR_SAT2;
1660 }
1661 else {
1662 raw = filePixelValue;
1663 }
1664 }
1665 }
1666 else {
1667 if(bufferVal == NULL8)
1668 raw = NULL2;
1669 else if(bufferVal == LOW_INSTR_SAT8)
1670 raw = LOW_INSTR_SAT2;
1671 else if(bufferVal == LOW_REPR_SAT8)
1672 raw = LOW_REPR_SAT2;
1673 else if(bufferVal == HIGH_INSTR_SAT8)
1674 raw = HIGH_INSTR_SAT2;
1675 else if(bufferVal == HIGH_REPR_SAT8)
1676 raw = HIGH_REPR_SAT2;
1677 else
1678 raw = LOW_REPR_SAT2;
1679 }
1680
1681 ((short *)chunkBuf)[chunkIndex] =
1682 m_byteSwapper ? m_byteSwapper->ShortInt(&raw) : raw;
1683 }
1684
1685 else if(m_pixelType == UnsignedInteger) {
1686
1687 unsigned int raw;
1688
1689 if(bufferVal >= VALID_MINUI4) {
1690 double filePixelValueDbl = (bufferVal - m_base) /
1692 if(filePixelValueDbl < VALID_MINUI4 - 0.5) {
1693 raw = LOW_REPR_SATUI4;
1694 }
1695 if(filePixelValueDbl > VALID_MAXUI4) {
1696 raw = HIGH_REPR_SATUI4;
1697 }
1698 else {
1699 unsigned int filePixelValue = (unsigned int)round(filePixelValueDbl);
1700
1701 if(filePixelValue < VALID_MINUI4) {
1702 raw = LOW_REPR_SATUI4;
1703 }
1704 else if(filePixelValue > VALID_MAXUI4) {
1705 raw = HIGH_REPR_SATUI4;
1706 }
1707 else {
1708 raw = filePixelValue;
1709 }
1710 }
1711 }
1712 else {
1713 if(bufferVal == NULL8)
1714 raw = NULLUI4;
1715 else if(bufferVal == LOW_INSTR_SAT8)
1716 raw = LOW_INSTR_SATUI4;
1717 else if(bufferVal == LOW_REPR_SAT8)
1718 raw = LOW_REPR_SATUI4;
1719 else if(bufferVal == HIGH_INSTR_SAT8)
1720 raw = HIGH_INSTR_SATUI4;
1721 else if(bufferVal == HIGH_REPR_SAT8)
1722 raw = HIGH_REPR_SATUI4;
1723 else
1724 raw = LOW_REPR_SATUI4;
1725 }
1726
1727 ((unsigned int *)chunkBuf)[chunkIndex] =
1728 m_byteSwapper ? m_byteSwapper->Uint32_t(&raw) : raw;
1729
1730 }
1731
1732
1733 else if(m_pixelType == UnsignedWord) {
1734 unsigned short raw;
1735
1736 if(bufferVal >= VALID_MIN8) {
1737 double filePixelValueDbl = (bufferVal - m_base) /
1739 if(filePixelValueDbl < VALID_MINU2 - 0.5) {
1740 raw = LOW_REPR_SATU2;
1741 }
1742 if(filePixelValueDbl > VALID_MAXU2 + 0.5) {
1743 raw = HIGH_REPR_SATU2;
1744 }
1745 else {
1746 int filePixelValue = (int)round(filePixelValueDbl);
1747
1748 if(filePixelValue < VALID_MINU2) {
1749 raw = LOW_REPR_SATU2;
1750 }
1751 else if(filePixelValue > VALID_MAXU2) {
1752 raw = HIGH_REPR_SATU2;
1753 }
1754 else {
1755 raw = filePixelValue;
1756 }
1757 }
1758 }
1759 else {
1760 if(bufferVal == NULL8)
1761 raw = NULLU2;
1762 else if(bufferVal == LOW_INSTR_SAT8)
1763 raw = LOW_INSTR_SATU2;
1764 else if(bufferVal == LOW_REPR_SAT8)
1765 raw = LOW_REPR_SATU2;
1766 else if(bufferVal == HIGH_INSTR_SAT8)
1767 raw = HIGH_INSTR_SATU2;
1768 else if(bufferVal == HIGH_REPR_SAT8)
1769 raw = HIGH_REPR_SATU2;
1770 else
1771 raw = LOW_REPR_SATU2;
1772 }
1773
1774 ((unsigned short *)chunkBuf)[chunkIndex] =
1776 }
1777 else if(m_pixelType == UnsignedByte) {
1778 unsigned char raw;
1779
1780 if(bufferVal >= VALID_MIN8) {
1781 double filePixelValueDbl = (bufferVal - m_base) /
1783 if(filePixelValueDbl < VALID_MIN1 - 0.5) {
1784 raw = LOW_REPR_SAT1;
1785 }
1786 else if(filePixelValueDbl > VALID_MAX1 + 0.5) {
1787 raw = HIGH_REPR_SAT1;
1788 }
1789 else {
1790 int filePixelValue = (int)(filePixelValueDbl + 0.5);
1791 if(filePixelValue < VALID_MIN1) {
1792 raw = LOW_REPR_SAT1;
1793 }
1794 else if(filePixelValue > VALID_MAX1) {
1795 raw = HIGH_REPR_SAT1;
1796 }
1797 else {
1798 raw = (unsigned char)(filePixelValue);
1799 }
1800 }
1801 }
1802 else {
1803 if(bufferVal == NULL8)
1804 raw = NULL1;
1805 else if(bufferVal == LOW_INSTR_SAT8)
1806 raw = LOW_INSTR_SAT1;
1807 else if(bufferVal == LOW_REPR_SAT8)
1808 raw = LOW_REPR_SAT1;
1809 else if(bufferVal == HIGH_INSTR_SAT8)
1810 raw = HIGH_INSTR_SAT1;
1811 else if(bufferVal == HIGH_REPR_SAT8)
1812 raw = HIGH_REPR_SAT1;
1813 else
1814 raw = LOW_REPR_SAT1;
1815 }
1816
1817 ((unsigned char *)chunkBuf)[chunkIndex] = raw;
1818 }
1819
1820 bufferIndex ++;
1821 }
1822 }
1823 }
1824 }
1825 }
1826
1827
1832 if(!m_dataIsOnDiskMap) {
1833 IString msg = "Cannot call CubeIoHandler::writeNullDataToDisk unless "
1834 "data is not already on disk (Cube::Create was called)";
1835 throw IException(IException::Programmer, msg, _FILEINFO_);
1836 }
1837
1838 int numChunks = getChunkCount();
1839 for(int i = 0; i < numChunks; i++) {
1840 if(!(*m_dataIsOnDiskMap)[i]) {
1841 RawCubeChunk *nullChunk = getNullChunk(i);
1842 (const_cast<CubeIoHandler *>(this))->writeRaw(*nullChunk);
1843 (*m_dataIsOnDiskMap)[i] = true;
1844
1845 delete nullChunk;
1846 nullChunk = NULL;
1847 }
1848 }
1849 }
1850
1851
1866 CubeIoHandler * ioHandler, QList<Buffer *> buffersToWrite) {
1867 m_ioHandler = ioHandler;
1868 m_buffersToWrite = new QList<Buffer *>(buffersToWrite);
1869 m_timer = new QTime;
1870 m_timer->start();
1871
1873 }
1874
1875
1887 int elapsedMs = m_timer->elapsed();
1888 int idealFlushElapsedTime = 100; // ms
1889
1890 // We want to aim our flush size at 100ms, so adjust accordingly to aim at
1891 // our target. This method seems to be extremely effective because
1892 // we maximize our I/O calls when caching is interfering and normalize
1893 // them otherwise.
1894 int msOffIdeal = elapsedMs - idealFlushElapsedTime;
1895 double percentOffIdeal = msOffIdeal / (double)idealFlushElapsedTime;
1896
1897 // flush size is bounded to [32, 5000]
1898 int currentCacheSize = m_ioHandler->m_idealFlushSize;
1899 int desiredAdjustment = -1 * currentCacheSize * percentOffIdeal;
1900 int desiredCacheSize = (int)(currentCacheSize + desiredAdjustment);
1901 m_ioHandler->m_idealFlushSize = (int)(qMin(5000,
1902 qMax(32, desiredCacheSize)));
1903
1904 delete m_timer;
1905 m_timer = NULL;
1906
1907 m_ioHandler->m_writeThreadMutex->unlock();
1908 m_ioHandler = NULL;
1909
1910 delete m_buffersToWrite;
1911 m_buffersToWrite = NULL;
1912 }
1913
1914
1921 // Sorting the buffers didn't seem to have a large positive impact on speed,
1922 // but does increase complexity so it's disabled.
1923// QList<Buffer * > buffersToWrite(*m_buffersToWrite);
1924// qSort(buffersToWrite.begin(), buffersToWrite.end(), bufferLessThan);
1925
1926 // If the buffers have any overlap at all then we can't sort them and still
1927 // guarantee the last write() call makes it into the correct place. The
1928 // bufferLessThan is guaranteed to return false if there is overlap.
1929// bool sortable = true;
1930// for (int i = 1; sortable && i < buffersToWrite.size(); i++) {
1931// if (!bufferLessThan(buffersToWrite[i - 1], buffersToWrite[i])) {
1932// sortable = false;
1933// }
1934// }
1935
1936// if (!sortable) {
1937// buffersToWrite = *m_buffersToWrite;
1938// }
1939
1940 foreach (Buffer * bufferToWrite, *m_buffersToWrite) {
1941 m_ioHandler->synchronousWrite(*bufferToWrite);
1942 delete bufferToWrite;
1943 }
1944
1945 m_buffersToWrite->clear();
1946 m_ioHandler->m_dataFile->flush();
1947 }
1948}
Represents a 3D area (a 3D "cube")
Definition Area3D.h:29
Buffer for containing a three dimensional section of an image.
Definition Brick.h:45
Buffer for reading and writing cube data.
Definition Buffer.h:53
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition Buffer.h:138
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition Buffer.cpp:162
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition Buffer.h:88
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
This stores the results of the caching algorithm.
QList< RawCubeChunk * > getChunksToFree() const
bool algorithmUnderstoodData() const
If this is true, then the results (be them empty or not) should be considered valid.
This is the parent of the caching algorithms.
virtual CacheResult recommendChunksToFree(QList< RawCubeChunk * > allocated, QList< RawCubeChunk * > justUsed, const Buffer &justRequested)=0
Call this to determine which chunks should be freed from memory.
This class is designed to handle write() asynchronously.
QTime * m_timer
Used to calculate the lifetime of an instance of this class.
BufferToChunkWriter(CubeIoHandler *ioHandler, QList< Buffer * > buffersToWrite)
Create a BufferToChunkWriter which is designed to asynchronously move the given buffers into the cube...
~BufferToChunkWriter()
We're done writing our buffers into the cube, clean up.
QList< Buffer * > * m_buffersToWrite
The buffers that need pushed into the IO handler; we own these.
void run()
This is the asynchronous computation.
CubeIoHandler * m_ioHandler
The IO Handler instance to put the buffers into.
Handles converting buffers to and from disk.
bool m_useOptimizedCubeWrite
This is true if the Isis preference for the cube write thread is optimized.
QList< CubeCachingAlgorithm * > * m_cachingAlgorithms
The caching algorithms to use, in order of priority.
int m_consecutiveOverflowCount
How many times the write cache has overflown in a row.
int m_numBands
The number of physical bands in the cube.
int m_bandsInChunk
The number of physical bands in a cube chunk.
void clearCache(bool blockForWriteCache=true) const
Free all cube chunks (cached cube data) from memory and write them to disk.
int m_samplesInChunk
The number of samples in a cube chunk.
QMap< int, bool > * m_dataIsOnDiskMap
The map from chunk index to on-disk status, all true if not allocated.
void freeChunk(RawCubeChunk *chunkToFree) const
If the chunk is dirty, then we write it to disk.
int m_linesInChunk
The number of lines in a cube chunk.
virtual ~CubeIoHandler()
Cleans up all allocated memory.
void addCachingAlgorithm(CubeCachingAlgorithm *algorithm)
This will add the given caching algorithm to the list of attempted caching algorithms.
int m_numSamples
The number of samples in the cube.
QMutex * dataFileMutex()
Get the mutex that this IO handler is using around I/Os on the given data file.
void blockUntilThreadPoolEmpty() const
This blocks (doesn't return) until the number of active runnables in the thread pool goes to 0.
QPair< QList< RawCubeChunk * >, QList< int > > findCubeChunks(int startSample, int numSamples, int startLine, int numLines, int startBand, int numBands) const
Get the cube chunks that correspond to the given cube area.
PixelType pixelType() const
void writeNullDataToDisk() const
Write all NULL cube chunks that have not yet been accessed to disk.
QMutex * m_writeThreadMutex
This enables us to block while the write thread is working.
QList< int > * m_virtualBands
Converts from virtual band to physical band.
volatile int m_idealFlushSize
Ideal write cache flush size.
int getChunkCountInLineDimension() const
QList< RawCubeChunk * > * m_lastProcessByLineChunks
This is an optimization for process by line.
bool m_lastOperationWasWrite
If the last operation was a write then we need to flush the cache when reading.
void setChunkSizes(int numSamples, int numLines, int numBands)
This should be called once from the child constructor.
PixelType m_pixelType
The format of each DN in the cube.
QByteArray * m_nullChunkData
A raw cube chunk's data when it was all NULL's. Used for speed.
int getLineCountInChunk() const
double m_multiplier
The multiplicative factor of the data on disk.
int m_numLines
The number of lines in the cube.
int getChunkCountInSampleDimension() const
void minimizeCache(const QList< RawCubeChunk * > &justUsed, const Buffer &justRequested) const
Apply the caching algorithms and get rid of excess cube data in memory.
QFile * m_dataFile
The file containing cube data.
int getChunkCountInBandDimension() const
QPair< QMutex *, QList< Buffer * > > * m_writeCache
These are the buffers we need to write to raw cube chunks.
double m_base
The additive offset of the data on disk.
CubeIoHandler(QFile *dataFile, const QList< int > *virtualBandList, const Pvl &label, bool alreadyOnDisk)
Creates a new CubeIoHandler using a RegionalCachingAlgorithm.
BigInt getBytesPerChunk() const
QMap< int, RawCubeChunk * > * m_rawData
The map from chunk index to chunk for cached data.
QThreadPool * m_ioThreadPool
This contains threads for doing cube writes (and later maybe cube reads).
BigInt getDataStartByte() const
void findIntersection(const RawCubeChunk &cube1, const Buffer &cube2, int &startX, int &startY, int &startZ, int &endX, int &endY, int &endZ) const
Find the intersection between the buffer area and the cube chunk.
void flushWriteCache(bool force=false) const
This attempts to write the so-far-unwritten buffers from m_writeCache into the cube's RawCubeChunk ca...
void getChunkPlacement(int chunkIndex, int &startSample, int &startLine, int &startBand, int &endSample, int &endLine, int &endBand) const
Get the X/Y/Z (Sample,Line,Band) range of the chunk at the given index.
void read(Buffer &bufferToFill) const
Read cube data from disk into the buffer.
RawCubeChunk * getNullChunk(int chunkIndex) const
This creates a chunk filled with NULLs whose placement is at chunkIndex's position.
void write(const Buffer &bufferToWrite)
Write buffer data into the cube data on disk.
BigInt m_startByte
The start byte of the cube data.
void setVirtualBands(const QList< int > *virtualBandList)
This changes the virtual band list.
RawCubeChunk * getChunk(int chunkIndex, bool allocateIfNecessary) const
Retrieve the cached chunk at the given chunk index, if there is one.
static bool bufferLessThan(Buffer *const &lhs, Buffer *const &rhs)
This is used for sorting buffers into the most efficient write order.
int getSampleCountInChunk() const
BigInt getDataSize() const
int getBandCountInChunk() const
void writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index) const
Write the intersecting area of the buffer into the chunk.
void synchronousWrite(const Buffer &bufferToWrite)
This method takes the given buffer and synchronously puts it into the Cube's cache.
EndianSwapper * m_byteSwapper
A helper that swaps byte order to and from file order.
void writeIntoDouble(const RawCubeChunk &chunk, Buffer &output, int startIndex) const
Write the intersecting area of the chunk into the buffer.
int getChunkIndex(const RawCubeChunk &) const
Given a chunk, what's its index in the file.
Displacement is a signed length, usually in meters.
@ Pixels
The distance is being specified in pixels.
Distance measurement, usually in meters.
Definition Distance.h:34
@ Pixels
The distance is being specified in pixels.
Definition Distance.h:47
float Float(void *buf)
Swaps a floating point value.
short int ShortInt(void *buf)
Swaps a short integer value.
uint32_t Uint32_t(void *buf)
Swaps a 32bit unsigned integer.
unsigned short int UnsignedShortInt(void *buf)
Swaps an unsigned short integer value.
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
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
Contains Pvl Groups and Pvl Objects.
Definition PvlObject.h:61
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition PvlObject.h:276
A section of raw data on the disk.
int sampleCount() const
QByteArray & getRawData() const
int getStartSample() const
void setDirty(bool dirty)
Sets the chunk's dirty flag, indicating whether or not the chunk's data matches the data that is on d...
int bandCount() const
void setRawData(QByteArray rawData)
Sets the chunk's raw data.
int getStartBand() const
int getStartLine() const
int lineCount() const
This algorithm recommends chunks to be freed that are not within the last IO.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
int SizeOf(Isis::PixelType pixelType)
Returns the number of bytes of the specified PixelType.
Definition PixelType.h:46
Isis::PixelType PixelTypeEnumeration(const QString &type)
Returns PixelType enumeration given a string.
Definition PixelType.h:89
const double Null
Value for an Isis Null pixel.
long long int BigInt
Big int.
Definition Constants.h:49
PixelType
Enumerations for Isis Pixel Types.
Definition PixelType.h:27
Namespace for the standard library.