Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
CubeIoHandler.cpp
1
5
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 <QElapsedTimer>
22
23#include "Area3D.h"
24#include "Brick.h"
25#include "CubeCachingAlgorithm.h"
26#include "Displacement.h"
27#include "Distance.h"
28#include "IEndian.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 ImageIoHandler(virtualBandList) {
64 m_byteSwapper = NULL;
66 m_dataIsOnDiskMap = NULL;
67 m_rawData = NULL;
68 m_nullChunkData = NULL;
70 m_writeCache = NULL;
71 m_ioThreadPool = NULL;
72
73 try {
74 if (!dataFile) {
75 IString msg = "Cannot create a CubeIoHandler with a NULL data file";
76 throw IException(IException::Programmer, msg, _FILEINFO_);
77 }
78
80
81 PvlGroup &performancePrefs =
82 Preference::Preferences().findGroup("Performance");
83 IString cubeWritePerfOpt = performancePrefs["CubeWriteThread"][0];
84 m_useOptimizedCubeWrite = (cubeWritePerfOpt.DownCase() == "optimized");
85 if ((m_useOptimizedCubeWrite && !alreadyOnDisk) ||
86 cubeWritePerfOpt.DownCase() == "always") {
87 m_ioThreadPool = new QThreadPool;
88 m_ioThreadPool->setMaxThreadCount(1);
89 }
90
94 m_writeCache = new QPair< QMutex *, QList<Buffer *> >;
95 m_writeCache->first = new QMutex;
96 m_writeThreadMutex = new QMutex;
97
99
101
102 m_dataFile = dataFile;
103
104 const PvlObject &core = label.findObject("IsisCube").findObject("Core");
105 const PvlGroup &pixelGroup = core.findGroup("Pixels");
106
107 QString byteOrderStr = pixelGroup.findKeyword("ByteOrder")[0];
109 byteOrderStr.toUpper());
110 m_base = pixelGroup.findKeyword("Base");
111 m_multiplier = pixelGroup.findKeyword("Multiplier");
112 m_pixelType = PixelTypeEnumeration(pixelGroup.findKeyword("Type"));
113
114 // If the byte swapper isn't going to do anything, then get rid of it
115 // because it's quicker to check for a NULL byte swapper member than to
116 // call a swap that won't do anything.
117 if(!m_byteSwapper->willSwap()) {
118 delete m_byteSwapper;
119 m_byteSwapper = NULL;
120 }
121
122 const PvlGroup &dimensions = core.findGroup("Dimensions");
123 m_numSamples = dimensions.findKeyword("Samples");
124 m_numLines = dimensions.findKeyword("Lines");
125 m_numBands = dimensions.findKeyword("Bands");
126
127 m_startByte = (int)core.findKeyword("StartByte") - 1;
128
129 m_samplesInChunk = -1;
130 m_linesInChunk = -1;
131 m_bandsInChunk = -1;
132
133 if(!alreadyOnDisk) {
135 }
136 }
137 catch(IException &e) {
138 IString msg = "Constructing CubeIoHandler failed";
139 throw IException(e, IException::Programmer, msg, _FILEINFO_);
140 }
141 catch(...) {
142 IString msg = "Constructing CubeIoHandler failed";
143 throw IException(IException::Programmer, msg, _FILEINFO_);
144 }
145 }
146
147
153
154 if (m_ioThreadPool)
155 m_ioThreadPool->waitForDone();
156
157 delete m_ioThreadPool;
158 m_ioThreadPool = NULL;
159
160 delete m_dataIsOnDiskMap;
161 m_dataIsOnDiskMap = NULL;
162
164 QListIterator<CubeCachingAlgorithm *> it(*m_cachingAlgorithms);
165 while (it.hasNext()) {
166 delete it.next();
167 }
168 delete m_cachingAlgorithms;
169 m_cachingAlgorithms = NULL;
170 }
171
172 if (m_rawData) {
173 QMapIterator<int, RawCubeChunk *> it(*m_rawData);
174 while (it.hasNext()) {
175 // Unwritten data here means it cannot be written :(
176 it.next();
177
178 if(it.value())
179 delete it.value();
180 }
181 delete m_rawData;
182 m_rawData = NULL;
183 }
184
185 if (m_writeCache) {
186 delete m_writeCache->first;
187 m_writeCache->first = NULL;
188
189 for (int i = 0; i < m_writeCache->second.size(); i++) {
190 delete m_writeCache->second[i];
191 }
192 m_writeCache->second.clear();
193
194 delete m_writeCache;
195 m_writeCache = NULL;
196 }
197
198 delete m_byteSwapper;
199 m_byteSwapper = NULL;
200
201 delete m_nullChunkData;
202 m_nullChunkData = NULL;
203
206 }
207
208
217 void CubeIoHandler::read(Buffer &bufferToFill) const {
218 // We need to record the current chunk count size so we can use
219 // it to evaluate if the cache should be minimized
220 int lastChunkCount = m_rawData->size();
221
223 // Do the remaining writes
224 flushWriteCache(true);
225
227
228 // Stop backgrounding writes now, we don't want to keep incurring this
229 // penalty.
231 delete m_ioThreadPool;
232 m_ioThreadPool = NULL;
233 }
234 }
235
236 QMutexLocker lock(m_writeThreadMutex);
237
238 // NON-THREADED CUBE READ
239 QList<RawCubeChunk *> cubeChunks;
240 QList<int > chunkBands;
241
242 int bufferSampleCount = bufferToFill.SampleDimension();
243 int bufferLineCount = bufferToFill.LineDimension();
244 int bufferBandCount = bufferToFill.BandDimension();
245
246 // our chunk dimensions are same as buffer shape dimensions
247 if (bufferSampleCount == m_samplesInChunk &&
248 bufferLineCount == m_linesInChunk &&
249 bufferBandCount == m_bandsInChunk) {
250 int bufferStartSample = bufferToFill.Sample();
251 int bufferStartLine = bufferToFill.Line();
252 int bufferStartBand = bufferToFill.Band();
253
254 int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
255 int bufferEndLine = bufferStartLine + bufferLineCount - 1;
256 int bufferEndBand = bufferStartBand + bufferBandCount - 1;
257
258 // make sure we access the correct band
259 int startBand = bufferStartBand - 1;
260 if (m_virtualBands)
261 startBand = m_virtualBands->at(bufferStartBand - 1);
262
263 int expectedChunkIndex =
264 ((bufferStartSample - 1) / getSampleCountInChunk()) +
265 ((bufferStartLine - 1) / getLineCountInChunk()) *
267 ((startBand - 1) / getBandCountInChunk()) *
270
271 int chunkStartSample, chunkStartLine, chunkStartBand,
272 chunkEndSample, chunkEndLine, chunkEndBand;
273
274 getChunkPlacement(expectedChunkIndex,
275 chunkStartSample, chunkStartLine, chunkStartBand,
276 chunkEndSample, chunkEndLine, chunkEndBand);
277
278 if (chunkStartSample == bufferStartSample &&
279 chunkStartLine == bufferStartLine &&
280 chunkStartBand == bufferStartBand &&
281 chunkEndSample == bufferEndSample &&
282 chunkEndLine == bufferEndLine &&
283 chunkEndBand == bufferEndBand) {
284 cubeChunks.append(getChunk(expectedChunkIndex, true));
285 chunkBands.append(cubeChunks.last()->getStartBand());
286 }
287 }
288
289 if (cubeChunks.empty()) {
290 // We can't guarantee our cube chunks will encompass the buffer
291 // if the buffer goes beyond the cube bounds.
292 for(int i = 0; i < bufferToFill.size(); i++) {
293 bufferToFill[i] = Null;
294 }
295
296 QPair< QList<RawCubeChunk *>, QList<int> > chunkInfo;
297 chunkInfo = findCubeChunks(
298 bufferToFill.Sample(), bufferSampleCount,
299 bufferToFill.Line(), bufferLineCount,
300 bufferToFill.Band(), bufferBandCount);
301 cubeChunks = chunkInfo.first;
302 chunkBands = chunkInfo.second;
303 }
304
305 for (int i = 0; i < cubeChunks.size(); i++) {
306 writeIntoDouble(*cubeChunks[i], bufferToFill, chunkBands[i]);
307 }
308
309 // Minimize the cache if it changed in size
310 if (lastChunkCount != m_rawData->size()) {
311 minimizeCache(cubeChunks, bufferToFill);
312 }
313 }
314
315
325 void CubeIoHandler::write(const Buffer &bufferToWrite) {
327
328 if (m_ioThreadPool) {
329 // THREADED CUBE WRITE
330 Buffer * copy = new Buffer(bufferToWrite);
331 {
332 QMutexLocker locker(m_writeCache->first);
333 m_writeCache->second.append(copy);
334 }
335
337 }
338 else {
339 QMutexLocker lock(m_writeThreadMutex);
340 // NON-THREADED CUBE WRITE
341 synchronousWrite(bufferToWrite);
342 }
343 }
344
345
356 m_cachingAlgorithms->prepend(algorithm);
357 }
358
359
370 void CubeIoHandler::clearCache(bool blockForWriteCache) const {
371 if (blockForWriteCache) {
372 // Start the rest of the writes
373 flushWriteCache(true);
374 }
375
376 // If this map is allocated, then this is a brand new cube and we need to
377 // make sure it's filled with data or NULLs.
380 }
381
382 // This should be allocated. This is a list of the cached cube data.
383 // Write it all to disk.
384 if (m_rawData) {
385 QMapIterator<int, RawCubeChunk *> it(*m_rawData);
386 while (it.hasNext()) {
387 it.next();
388
389 if(it.value()) {
390 if(it.value()->isDirty()) {
391 (const_cast<CubeIoHandler *>(this))->writeRaw(*it.value());
392 }
393
394 delete it.value();
395 }
396 }
397
398 m_rawData->clear();
399 }
400
404 }
405 }
406
407
413 return (BigInt)getChunkCountInSampleDimension() *
416 (BigInt)getBytesPerChunk();
417 }
418
423 return m_numBands;
424 }
425
426
431 return m_bandsInChunk;
432 }
433
434
446
447
453 return (int)ceil((double)m_numBands / (double)m_bandsInChunk);
454 }
455
456
462 return (int)ceil((double)m_numLines / (double)m_linesInChunk);
463 }
464
465
471 return (int)ceil((double)m_numSamples / (double)m_samplesInChunk);
472 }
473
474
487
488 int sampleIndex = (chunk.getStartSample() - 1) / getSampleCountInChunk();
489 int lineIndex = (chunk.getStartLine() - 1) / getLineCountInChunk();
490 int bandIndex = (chunk.getStartBand() - 1) / getBandCountInChunk();
491
492 int indexInBand =
493 sampleIndex + lineIndex * getChunkCountInSampleDimension();
494 int indexOffsetToBand = bandIndex * getChunkCountInSampleDimension() *
496
497 return indexOffsetToBand + indexInBand;
498 }
499
500
505 return m_startByte;
506 }
507
508
516 return m_dataFile;
517 }
518
519
525 return m_numLines;
526 }
527
528
533 return m_linesInChunk;
534 }
535
536
540 PixelType CubeIoHandler::pixelType() const {
541 return m_pixelType;
542 }
543
544
550 return m_numSamples;
551 }
552
553
560
561
575 int numSamples, int numLines, int numBands) {
576 bool success = false;
577 IString msg;
578
579 if(m_samplesInChunk != -1 || m_linesInChunk != -1 || m_bandsInChunk != -1) {
580 IString msg = "You cannot change the chunk sizes once set";
581 }
582 else if(numSamples < 1) {
583 msg = "Negative and zero chunk sizes are not supported, samples per chunk"
584 " cannot be [" + IString(numSamples) + "]";
585 }
586 else if(numLines < 1) {
587 msg = "Negative and zero chunk sizes are not supported, lines per chunk "
588 "cannot be [" + IString(numLines) + "]";
589 }
590 else if(numBands < 1) {
591 msg = "Negative and zero chunk sizes are not supported, lines per chunk "
592 "cannot be [" + IString(numBands) + "]";
593 }
594 else {
595 success = true;
596 }
597
598 if(success) {
599 m_samplesInChunk = numSamples;
600 m_linesInChunk = numLines;
601 m_bandsInChunk = numBands;
602
605 }
606 else if(m_dataFile->size() < getDataStartByte() + getDataSize()) {
607 success = false;
608 msg = "File size [" + IString((BigInt)m_dataFile->size()) +
609 " bytes] not big enough to hold data [" +
610 IString(getDataStartByte() + getDataSize()) + " bytes] where the "
611 "offset to the cube data is [" + IString(getDataStartByte()) +
612 " bytes]";
613 }
614 }
615 else {
616 throw IException(IException::Programmer, msg, _FILEINFO_);
617 }
618 }
619
620
628 if (m_ioThreadPool) {
629 QMutexLocker lock(m_writeThreadMutex);
630 }
631 }
632
633
641 bool CubeIoHandler::bufferLessThan(Buffer * const &lhs, Buffer * const &rhs) {
642 bool lessThan = false;
643
644 // If there is any overlap then we need to return false due to it being
645 // ambiguous.
646 Area3D lhsArea(
653 Area3D rhsArea(
660
661 if (!lhsArea.intersect(rhsArea).isValid()) {
662 if (lhs->Band() != rhs->Band()) {
663 lessThan = lhs->Band() < rhs->Band();
664 }
665 else if (lhs->Line() != rhs->Line()) {
666 lessThan = lhs->Line() < rhs->Line();
667 }
668 else if (lhs->Sample() != rhs->Sample()) {
669 lessThan = lhs->Sample() < rhs->Sample();
670 }
671 }
672
673 return lessThan;
674 }
675
676
690 QPair< QList<RawCubeChunk *>, QList<int> > CubeIoHandler::findCubeChunks(int startSample,
691 int numSamples, int startLine, int numLines, int startBand,
692 int numBands) const {
693 QList<RawCubeChunk *> results;
694 QList<int> resultBands;
695/************************************************************************CHANGED THIS!!!!!!!!******/
696 int lastBand = startBand + numBands - 1;
697// int lastBand = min(startBand + numBands - 1,
698// bandCount());
699/************************************************************************CHANGED THIS!!!!!!!!******/
700
701 QRect areaInBand(
702 QPoint(max(startSample, 1),
703 max(startLine, 1)),
704 QPoint(min(startSample + numSamples - 1,
705 sampleCount()),
706 min(startLine + numLines - 1,
707 lineCount())));
708
709 // We are considering only 1 band at a time.. we can't use m_bandsInChunk
710 // because of virtual bands, but every extra loop should not need extra
711 // IO.
712 for(int band = startBand; band <= lastBand; band ++) {
713 // This is the user-requested area in this band
714 QRect areaLeftInBand(areaInBand);
715
716 int actualBand = band;
717
718 if(m_virtualBands) {
719 if (band < 1 || band > m_virtualBands->size())
720 actualBand = 0;
721 else
722 actualBand = (m_virtualBands->at(band - 1) - 1) / m_bandsInChunk + 1;
723 }
724 // We will be consuming areaLeftInBand until we've got all of the area
725 // requested.
726 while(!areaLeftInBand.isEmpty()) {
774 int areaStartLine = areaLeftInBand.top();
775 int areaStartSample = areaLeftInBand.left();
776
777 int initialChunkXPos = (areaStartSample - 1) / m_samplesInChunk;
778 int initialChunkYPos = (areaStartLine - 1) / m_linesInChunk;
779 int initialChunkZPos = (actualBand - 1) / m_bandsInChunk;
780 int initialChunkBand = initialChunkZPos * m_bandsInChunk + 1;
781
782
783 QRect chunkRect(initialChunkXPos * m_samplesInChunk + 1,
784 initialChunkYPos * m_linesInChunk + 1,
786
787 // The chunk rectangle must intersect the remaining area that is in the
788 // current band, and the chunk's initial band must be between 1 and the
789 // number of physical bands in the cube (inclusive).
790 while(chunkRect.intersects(areaLeftInBand) &&
791 (initialChunkBand >= 1 && initialChunkBand <= bandCount())) {
792 int chunkXPos = (chunkRect.left() - 1) / m_samplesInChunk;
793 int chunkYPos = (chunkRect.top() - 1) / m_linesInChunk;
794 int chunkZPos = initialChunkZPos;
795
796 // We now have an X,Y,Z position for the chunk. What's its index?
797 int chunkIndex = chunkXPos +
798 (chunkYPos * getChunkCountInSampleDimension()) +
799 (chunkZPos * getChunkCountInSampleDimension() *
801
802 RawCubeChunk * newChunk = getChunk(chunkIndex, true);
803
804 results.append(newChunk);
805 resultBands.append(band);
806
807 chunkRect.moveLeft(chunkRect.right() + 1);
808 }
809
810 areaLeftInBand.setTop(chunkRect.bottom() + 1);
811 }
812 }
813
814 return QPair< QList<RawCubeChunk *>, QList<int> >(results, resultBands);
815 }
816
817
836 const RawCubeChunk &cube1, const Buffer &cube2,
837 int &startX, int &startY, int &startZ,
838 int &endX, int &endY, int &endZ) const {
839 // So we have 2 3D "cubes" (not Cube cubes but 3d areas) we need to
840 // intersect in order to figure out what chunk data goes into the output
841 // buffer.
842
843 // To find the band range we actually have to map all of the bands from
844 // virtual bands to physical bands
845 int startVBand = cube2.Band();
846 int endVBand = startVBand + cube2.BandDimension() - 1;
847
848 int startPhysicalBand = 0;
849 int endPhysicalBand = 0;
850
851 bool startVBandFound = false;
852 for(int virtualBand = startVBand; virtualBand <= endVBand; virtualBand ++) {
853 int physicalBand = virtualBand;
854
855 bool bandExists = true;
856 if(m_virtualBands) {
857 if (virtualBand < 1 || virtualBand > m_virtualBands->size())
858 bandExists = false;
859 else {
860 physicalBand = m_virtualBands->at(virtualBand - 1);
861 }
862 }
863
864 if (bandExists) {
865 if(!startVBandFound) {
866 startPhysicalBand = physicalBand;
867 endPhysicalBand = physicalBand;
868 startVBandFound = true;
869 }
870 else {
871 if(physicalBand < startPhysicalBand)
872 startPhysicalBand = physicalBand;
873
874 if(physicalBand > endPhysicalBand)
875 endPhysicalBand = physicalBand;
876 }
877 }
878 }
879
880 startX = max(cube1.getStartSample(), cube2.Sample());
881 startY = max(cube1.getStartLine(), cube2.Line());
882 startZ = max(cube1.getStartBand(), startPhysicalBand);
883 endX = min(cube1.getStartSample() + cube1.sampleCount() - 1,
884 cube2.Sample() + cube2.SampleDimension() - 1);
885 endY = min(cube1.getStartLine() + cube1.lineCount() - 1,
886 cube2.Line() + cube2.LineDimension() - 1);
887 endZ = min(cube1.getStartBand() + cube1.bandCount() - 1,
888 endPhysicalBand);
889 }
890
891
900 void CubeIoHandler::flushWriteCache(bool force) const {
901 if (m_ioThreadPool) {
902 bool shouldFlush = m_writeCache->second.size() >= m_idealFlushSize ||
903 force;
904 bool cacheOverflowing =
905 (m_writeCache->second.size() > m_idealFlushSize * 10);
906 bool shouldAndCanFlush = false;
907 bool forceStart = force;
908
909 if (shouldFlush) {
910 shouldAndCanFlush = m_writeThreadMutex->tryLock();
911 if (shouldAndCanFlush) {
912 m_writeThreadMutex->unlock();
913 }
914 }
915
916 if (cacheOverflowing && !shouldAndCanFlush) {
917 forceStart = true;
919 }
920
921 if (forceStart) {
923
924 if (m_writeCache->second.size() != 0) {
925 m_idealFlushSize = m_writeCache->second.size();
926 shouldFlush = true;
927 shouldAndCanFlush = true;
928 }
929 }
930 else if (!cacheOverflowing && shouldAndCanFlush) {
932 }
933
934 if (cacheOverflowing && m_useOptimizedCubeWrite) {
936
937 // If the process is very I/O bound, then write caching isn't helping
938 // anything. In fact, it hurts, so turn it off.
940 delete m_ioThreadPool;
941 m_ioThreadPool = NULL;
942 }
943
944 // Write it all synchronously.
945 foreach (Buffer *bufferToWrite, m_writeCache->second) {
946 const_cast<CubeIoHandler *>(this)->synchronousWrite(*bufferToWrite);
947 delete bufferToWrite;
948 }
949
950 m_writeCache->second.clear();
951 }
952
953 if (shouldAndCanFlush && m_ioThreadPool) {
954 QMutexLocker locker(m_writeCache->first);
955 QRunnable *writer = new BufferToChunkWriter(
956 const_cast<CubeIoHandler *>(this), m_writeCache->second);
957
958 m_ioThreadPool->start(writer);
959
960 m_writeCache->second.clear();
962 }
963
964 if (force) {
966 }
967 }
968 }
969
970
977 void CubeIoHandler::freeChunk(RawCubeChunk *chunkToFree) const {
978 if(chunkToFree && m_rawData) {
979 int chunkIndex = getChunkIndex(*chunkToFree);
980
981 m_rawData->erase(m_rawData->find(chunkIndex));
982
983 if(chunkToFree->isDirty())
984 (const_cast<CubeIoHandler *>(this))->writeRaw(*chunkToFree);
985
986 delete chunkToFree;
987
991 }
992 }
993 }
994
995
1006 bool allocateIfNecessary) const {
1007 RawCubeChunk *chunk = NULL;
1008
1009 if(m_rawData) {
1010 chunk = m_rawData->value(chunkIndex);
1011 }
1012
1013 if(allocateIfNecessary && !chunk) {
1014 if(m_dataIsOnDiskMap && !(*m_dataIsOnDiskMap)[chunkIndex]) {
1015 chunk = getNullChunk(chunkIndex);
1016 (*m_dataIsOnDiskMap)[chunkIndex] = true;
1017 }
1018 else {
1019 int startSample;
1020 int startLine;
1021 int startBand;
1022 int endSample;
1023 int endLine;
1024 int endBand;
1025 getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1026 endSample, endLine, endBand);
1027 chunk = new RawCubeChunk(startSample, startLine, startBand,
1028 endSample, endLine, endBand,
1030
1031 (const_cast<CubeIoHandler *>(this))->readRaw(*chunk);
1032 chunk->setDirty(false);
1033 }
1034
1035 (*m_rawData)[chunkIndex] = chunk;
1036 }
1037
1038 return chunk;
1039 }
1040
1041
1051
1052
1065 int &startSample, int &startLine, int &startBand,
1066 int &endSample, int &endLine, int &endBand) const {
1067 int chunkSampleIndex = chunkIndex % getChunkCountInSampleDimension();
1068
1069 chunkIndex =
1070 (chunkIndex - chunkSampleIndex) / getChunkCountInSampleDimension();
1071
1072 int chunkLineIndex = chunkIndex % getChunkCountInLineDimension();
1073 chunkIndex = (chunkIndex - chunkLineIndex) / getChunkCountInLineDimension();
1074
1075 int chunkBandIndex = chunkIndex;
1076
1077 startSample = chunkSampleIndex * getSampleCountInChunk() + 1;
1078 endSample = startSample + getSampleCountInChunk() - 1;
1079 startLine = chunkLineIndex * getLineCountInChunk() + 1;
1080 endLine = startLine + getLineCountInChunk() - 1;
1081 startBand = chunkBandIndex * getBandCountInChunk() + 1;
1082 endBand = startBand + getBandCountInChunk() - 1;
1083 }
1084
1085
1097 // Shouldn't ask for null chunks when the area has already been allocated
1098
1099 int startSample = 0;
1100 int startLine = 0;
1101 int startBand = 0;
1102
1103 int endSample = 0;
1104 int endLine = 0;
1105 int endBand = 0;
1106
1107 getChunkPlacement(chunkIndex, startSample, startLine, startBand,
1108 endSample, endLine, endBand);
1109
1110 RawCubeChunk *result = new RawCubeChunk(startSample, startLine, startBand,
1111 endSample, endLine, endBand, getBytesPerChunk());
1112
1113 if(!m_nullChunkData) {
1114 // the pixel type doesn't really matter, so pick something small
1115 Brick nullBuffer(result->sampleCount(),
1116 result->lineCount(),
1117 result->bandCount(),
1118 UnsignedByte);
1119
1120 nullBuffer.SetBasePosition(result->getStartSample(),
1121 result->getStartLine(),
1122 result->getStartBand());
1123 for(int i = 0; i < nullBuffer.size(); i++) {
1124 nullBuffer[i] = Null;
1125 }
1126
1127 writeIntoRaw(nullBuffer, *result, result->getStartBand());
1128 m_nullChunkData = new QByteArray(result->getRawData());
1129 }
1130 else {
1131 result->setRawData(*m_nullChunkData);
1132 }
1133
1134 result->setDirty(true);
1135 return result;
1136 }
1137
1138
1149 const Buffer &justRequested) const {
1150 // Since we have a lock on the cache, no newly created threads can utilize
1151 // or access any cache data until we're done.
1152 if (m_rawData->size() * getBytesPerChunk() > 1 * 1024 * 1024 ||
1153 m_cachingAlgorithms->size() > 1) {
1154 bool algorithmAccepted = false;
1155
1156 int algorithmIndex = 0;
1157 while(!algorithmAccepted &&
1158 algorithmIndex < m_cachingAlgorithms->size()) {
1159 CubeCachingAlgorithm *algorithm = (*m_cachingAlgorithms)[algorithmIndex];
1160
1162 algorithm->recommendChunksToFree(m_rawData->values(), justUsed,
1163 justRequested);
1164
1165 algorithmAccepted = result.algorithmUnderstoodData();
1166
1167 if(algorithmAccepted) {
1168 QList<RawCubeChunk *> chunksToFree = result.getChunksToFree();
1169
1170 RawCubeChunk *chunkToFree;
1171 foreach(chunkToFree, chunksToFree) {
1172 freeChunk(chunkToFree);
1173 }
1174 }
1175
1176 algorithmIndex ++;
1177 }
1178
1179 // Fall back - no algorithms liked us :(
1180 if(!algorithmAccepted && m_rawData->size() > 100) {
1181 // This (minimizeCache()) is typically executed in the Runnable thread.
1182 // We don't want to wait for ourselves.
1183 clearCache(false);
1184 }
1185 }
1186 }
1187
1188
1197 void CubeIoHandler::synchronousWrite(const Buffer &bufferToWrite) {
1198 QList<RawCubeChunk *> cubeChunks;
1199 QList<int> cubeChunkBands;
1200
1201 int bufferSampleCount = bufferToWrite.SampleDimension();
1202 int bufferLineCount = bufferToWrite.LineDimension();
1203 int bufferBandCount = bufferToWrite.BandDimension();
1204
1205 // process by line optimization
1207 m_lastProcessByLineChunks->size()) {
1208 // Not optimized yet, let's see if we can optimize
1209 if(bufferToWrite.Sample() == 1 &&
1210 bufferSampleCount == sampleCount() &&
1211 bufferLineCount == 1 &&
1212 bufferBandCount == 1) {
1213 // We look like a process by line, are we using the same chunks as
1214 // before?
1215 int bufferLine = bufferToWrite.Line();
1216 int chunkStartLine =
1217 (*m_lastProcessByLineChunks)[0]->getStartLine();
1218 int chunkLines =
1219 (*m_lastProcessByLineChunks)[0]->lineCount();
1220 int bufferBand = bufferToWrite.Band();
1221 int chunkStartBand =
1222 (*m_lastProcessByLineChunks)[0]->getStartBand();
1223 int chunkBands =
1224 (*m_lastProcessByLineChunks)[0]->bandCount();
1225
1226 if(bufferLine >= chunkStartLine &&
1227 bufferLine <= chunkStartLine + chunkLines - 1 &&
1228 bufferBand >= chunkStartBand &&
1229 bufferBand <= chunkStartBand + chunkBands - 1) {
1230 cubeChunks = *m_lastProcessByLineChunks;
1231 for (int i = 0; i < cubeChunks.size(); i++) {
1232 cubeChunkBands.append( cubeChunks[i]->getStartBand() );
1233 }
1234 }
1235 }
1236 }
1237 // Processing by chunk size
1238 else if (bufferSampleCount == m_samplesInChunk &&
1239 bufferLineCount == m_linesInChunk &&
1240 bufferBandCount == m_bandsInChunk) {
1241 int bufferStartSample = bufferToWrite.Sample();
1242 int bufferStartLine = bufferToWrite.Line();
1243 int bufferStartBand = bufferToWrite.Band();
1244
1245 int bufferEndSample = bufferStartSample + bufferSampleCount - 1;
1246 int bufferEndLine = bufferStartLine + bufferLineCount - 1;
1247 int bufferEndBand = bufferStartBand + bufferBandCount - 1;
1248
1249 int expectedChunkIndex =
1250 ((bufferStartSample - 1) / getSampleCountInChunk()) +
1251 ((bufferStartLine - 1) / getLineCountInChunk()) *
1253 ((bufferStartBand - 1) / getBandCountInChunk()) *
1256
1257 int chunkStartSample, chunkStartLine, chunkStartBand,
1258 chunkEndSample, chunkEndLine, chunkEndBand;
1259
1260 getChunkPlacement(expectedChunkIndex,
1261 chunkStartSample, chunkStartLine, chunkStartBand,
1262 chunkEndSample, chunkEndLine, chunkEndBand);
1263
1264 if (chunkStartSample == bufferStartSample &&
1265 chunkStartLine == bufferStartLine &&
1266 chunkStartBand == bufferStartBand &&
1267 chunkEndSample == bufferEndSample &&
1268 chunkEndLine == bufferEndLine &&
1269 chunkEndBand == bufferEndBand) {
1270 cubeChunks.append(getChunk(expectedChunkIndex, true));
1271 cubeChunkBands.append(cubeChunks.last()->getStartBand());
1272 }
1273 }
1274
1275 QPair< QList<RawCubeChunk *>, QList<int> > chunkInfo;
1276 if(cubeChunks.empty()) {
1277 chunkInfo = findCubeChunks(
1278 bufferToWrite.Sample(), bufferSampleCount,
1279 bufferToWrite.Line(), bufferLineCount,
1280 bufferToWrite.Band(), bufferBandCount);
1281 cubeChunks = chunkInfo.first;
1282 cubeChunkBands = chunkInfo.second;
1283 }
1284
1285 // process by line optimization
1286 if(bufferToWrite.Sample() == 1 &&
1287 bufferSampleCount == sampleCount() &&
1288 bufferLineCount == 1 &&
1289 bufferBandCount == 1) {
1292 new QList<RawCubeChunk *>(cubeChunks);
1293 else
1294 *m_lastProcessByLineChunks = cubeChunks;
1295 }
1296
1297 for(int i = 0; i < cubeChunks.size(); i++) {
1298 writeIntoRaw(bufferToWrite, *cubeChunks[i], cubeChunkBands[i]);
1299 }
1300
1301 minimizeCache(cubeChunks, bufferToWrite);
1302 }
1303
1304
1313 Buffer &output, int index) const {
1314 // The code in this method is highly optimized. Even the order of the if
1315 // statements will have a significant impact on performance if changed.
1316 // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1317 // writeIntoRaw(...). This is needed for performance gain. Any function
1318 // or method calls from within the x loop cause significant performance
1319 // decreases.
1320 int startX = 0;
1321 int startY = 0;
1322 int startZ = 0;
1323
1324 int endX = 0;
1325 int endY = 0;
1326 int endZ = 0;
1327
1328 findIntersection(chunk, output, startX, startY, startZ, endX, endY, endZ);
1329
1330 int bufferBand = output.Band();
1331 int bufferBands = output.BandDimension();
1332 int chunkStartSample = chunk.getStartSample();
1333 int chunkStartLine = chunk.getStartLine();
1334 int chunkStartBand = chunk.getStartBand();
1335 int chunkLineSize = chunk.sampleCount();
1336 int chunkBandSize = chunkLineSize * chunk.lineCount();
1337 //double *buffersDoubleBuf = output.p_buf;
1338 double *buffersDoubleBuf = output.DoubleBuffer();
1339 const char *chunkBuf = chunk.getRawData().data();
1340 char *buffersRawBuf = (char *)output.RawBuffer();
1341
1342 for(int z = startZ; z <= endZ; z++) {
1343 const int &bandIntoChunk = z - chunkStartBand;
1344 int virtualBand = z;
1345
1346 virtualBand = index;
1347
1348 if(virtualBand != 0 && virtualBand >= bufferBand &&
1349 virtualBand <= bufferBand + bufferBands - 1) {
1350
1351 for(int y = startY; y <= endY; y++) {
1352 const int &lineIntoChunk = y - chunkStartLine;
1353 int bufferIndex = output.Index(startX, y, virtualBand);
1354
1355 for(int x = startX; x <= endX; x++) {
1356 const int &sampleIntoChunk = x - chunkStartSample;
1357
1358 const int &chunkIndex = sampleIntoChunk +
1359 (chunkLineSize * lineIntoChunk) +
1360 (chunkBandSize * bandIntoChunk);
1361
1362 double &bufferVal = buffersDoubleBuf[bufferIndex];
1363
1364 if(m_pixelType == Real) {
1365 float raw = ((float *)chunkBuf)[chunkIndex];
1366 if(m_byteSwapper)
1367 raw = m_byteSwapper->Float(&raw);
1368
1369 if(raw >= VALID_MIN4) {
1370 bufferVal = (double) raw;
1371 }
1372 else {
1373 if(raw == NULL4)
1374 bufferVal = NULL8;
1375 else if(raw == LOW_INSTR_SAT4)
1376 bufferVal = LOW_INSTR_SAT8;
1377 else if(raw == LOW_REPR_SAT4)
1378 bufferVal = LOW_REPR_SAT8;
1379 else if(raw == HIGH_INSTR_SAT4)
1380 bufferVal = HIGH_INSTR_SAT8;
1381 else if(raw == HIGH_REPR_SAT4)
1382 bufferVal = HIGH_REPR_SAT8;
1383 else
1384 bufferVal = LOW_REPR_SAT8;
1385 }
1386
1387 ((float *)buffersRawBuf)[bufferIndex] = raw;
1388 }
1389
1390 else if(m_pixelType == SignedWord) {
1391 short raw = ((short *)chunkBuf)[chunkIndex];
1392 if(m_byteSwapper)
1393 raw = m_byteSwapper->ShortInt(&raw);
1394
1395 if(raw >= VALID_MIN2) {
1396 bufferVal = (double) raw * m_multiplier + m_base;
1397 }
1398 else {
1399 if(raw == NULL2)
1400 bufferVal = NULL8;
1401 else if(raw == LOW_INSTR_SAT2)
1402 bufferVal = LOW_INSTR_SAT8;
1403 else if(raw == LOW_REPR_SAT2)
1404 bufferVal = LOW_REPR_SAT8;
1405 else if(raw == HIGH_INSTR_SAT2)
1406 bufferVal = HIGH_INSTR_SAT8;
1407 else if(raw == HIGH_REPR_SAT2)
1408 bufferVal = HIGH_REPR_SAT8;
1409 else
1410 bufferVal = LOW_REPR_SAT8;
1411 }
1412
1413 ((short *)buffersRawBuf)[bufferIndex] = raw;
1414 }
1415
1416
1417 else if(m_pixelType == UnsignedWord) {
1418 unsigned short raw = ((unsigned short *)chunkBuf)[chunkIndex];
1419 if(m_byteSwapper)
1420 raw = m_byteSwapper->UnsignedShortInt(&raw);
1421
1422 if(raw >= VALID_MINU2) {
1423 bufferVal = (double) raw * m_multiplier + m_base;
1424 }
1425 else if (raw > VALID_MAXU2) {
1426 if(raw == HIGH_INSTR_SATU2)
1427 bufferVal = HIGH_INSTR_SAT8;
1428 else if(raw == HIGH_REPR_SATU2)
1429 bufferVal = HIGH_REPR_SAT8;
1430 else
1431 bufferVal = LOW_REPR_SAT8;
1432 }
1433 else {
1434 if(raw == NULLU2)
1435 bufferVal = NULL8;
1436 else if(raw == LOW_INSTR_SATU2)
1437 bufferVal = LOW_INSTR_SAT8;
1438 else if(raw == LOW_REPR_SATU2)
1439 bufferVal = LOW_REPR_SAT8;
1440 else
1441 bufferVal = LOW_REPR_SAT8;
1442 }
1443
1444 ((unsigned short *)buffersRawBuf)[bufferIndex] = raw;
1445 }
1446
1447 else if(m_pixelType == UnsignedInteger) {
1448
1449 unsigned int raw = ((unsigned int *)chunkBuf)[chunkIndex];
1450 if(m_byteSwapper)
1451 raw = m_byteSwapper->Uint32_t(&raw);
1452
1453 if(raw >= VALID_MINUI4) {
1454 bufferVal = (double) raw * m_multiplier + m_base;
1455 }
1456 else if (raw > VALID_MAXUI4) {
1457 if(raw == HIGH_INSTR_SATUI4)
1458 bufferVal = HIGH_INSTR_SAT8;
1459 else if(raw == HIGH_REPR_SATUI4)
1460 bufferVal = HIGH_REPR_SAT8;
1461 else
1462 bufferVal = LOW_REPR_SAT8;
1463 }
1464 else {
1465 if(raw == NULLUI4)
1466 bufferVal = NULL8;
1467 else if(raw == LOW_INSTR_SATUI4)
1468 bufferVal = LOW_INSTR_SAT8;
1469 else if(raw == LOW_REPR_SATUI4)
1470 bufferVal = LOW_REPR_SAT8;
1471 else
1472 bufferVal = LOW_REPR_SAT8;
1473 }
1474
1475 ((unsigned int *)buffersRawBuf)[bufferIndex] = raw;
1476
1477
1478
1479 }
1480
1481 else if(m_pixelType == UnsignedByte) {
1482 unsigned char raw = ((unsigned char *)chunkBuf)[chunkIndex];
1483
1484 if(raw == NULL1) {
1485 bufferVal = NULL8;
1486 }
1487 else if(raw == HIGH_REPR_SAT1) {
1488 bufferVal = HIGH_REPR_SAT8;
1489 }
1490 else {
1491 bufferVal = (double) raw * m_multiplier + m_base;
1492 }
1493
1494 ((unsigned char *)buffersRawBuf)[bufferIndex] = raw;
1495 }
1496
1497 bufferIndex++;
1498 }
1499 }
1500 }
1501 }
1502 }
1503
1504
1512 void CubeIoHandler::writeIntoRaw(const Buffer &buffer, RawCubeChunk &output, int index)
1513 const {
1514 // The code in this method is highly optimized. Even the order of the if
1515 // statements will have a significant impact on performance if changed.
1516 // Also, there is a lot of duplicate code in both writeIntoDouble(...) and
1517 // writeIntoRaw(...). This is needed for performance gain. Any function
1518 // or method calls from within the x loop cause significant performance
1519 // decreases.
1520 int startX = 0;
1521 int startY = 0;
1522 int startZ = 0;
1523
1524 int endX = 0;
1525 int endY = 0;
1526 int endZ = 0;
1527
1528 output.setDirty(true);
1529 findIntersection(output, buffer, startX, startY, startZ, endX, endY, endZ);
1530
1531 int bufferBand = buffer.Band();
1532 int bufferBands = buffer.BandDimension();
1533 int outputStartSample = output.getStartSample();
1534 int outputStartLine = output.getStartLine();
1535 int outputStartBand = output.getStartBand();
1536 int lineSize = output.sampleCount();
1537 int bandSize = lineSize * output.lineCount();
1538 double *buffersDoubleBuf = buffer.DoubleBuffer();
1539 char *chunkBuf = output.getRawData().data();
1540
1541 for(int z = startZ; z <= endZ; z++) {
1542 const int &bandIntoChunk = z - outputStartBand;
1543 int virtualBand = index;
1544
1545
1546 if(m_virtualBands) {
1547 virtualBand = m_virtualBands->indexOf(virtualBand) + 1;
1548 }
1549
1550 if(virtualBand != 0 && virtualBand >= bufferBand &&
1551 virtualBand <= bufferBand + bufferBands - 1) {
1552
1553 for(int y = startY; y <= endY; y++) {
1554 const int &lineIntoChunk = y - outputStartLine;
1555 int bufferIndex = buffer.Index(startX, y, virtualBand);
1556
1557 for(int x = startX; x <= endX; x++) {
1558 const int &sampleIntoChunk = x - outputStartSample;
1559
1560 const int &chunkIndex = sampleIntoChunk +
1561 (lineSize * lineIntoChunk) + (bandSize * bandIntoChunk);
1562
1563 double bufferVal = buffersDoubleBuf[bufferIndex];
1564
1565 if(m_pixelType == Real) {
1566 float raw = 0;
1567
1568 if(bufferVal >= VALID_MIN8) {
1569 double filePixelValueDbl = (bufferVal - m_base) /
1571
1572 if(filePixelValueDbl < (double) VALID_MIN4) {
1573 raw = LOW_REPR_SAT4;
1574 }
1575 else if(filePixelValueDbl > (double) VALID_MAX4) {
1576 raw = HIGH_REPR_SAT4;
1577 }
1578 else {
1579 raw = (float) filePixelValueDbl;
1580 }
1581 }
1582 else {
1583 if(bufferVal == NULL8)
1584 raw = NULL4;
1585 else if(bufferVal == LOW_INSTR_SAT8)
1586 raw = LOW_INSTR_SAT4;
1587 else if(bufferVal == LOW_REPR_SAT8)
1588 raw = LOW_REPR_SAT4;
1589 else if(bufferVal == HIGH_INSTR_SAT8)
1590 raw = HIGH_INSTR_SAT4;
1591 else if(bufferVal == HIGH_REPR_SAT8)
1592 raw = HIGH_REPR_SAT4;
1593 else
1594 raw = LOW_REPR_SAT4;
1595 }
1596 ((float *)chunkBuf)[chunkIndex] =
1597 m_byteSwapper ? m_byteSwapper->Float(&raw) : raw;
1598 }
1599
1600 else if(m_pixelType == SignedWord) {
1601 short raw;
1602
1603 if(bufferVal >= VALID_MIN8) {
1604 double filePixelValueDbl = (bufferVal - m_base) /
1606 if(filePixelValueDbl < VALID_MIN2 - 0.5) {
1607 raw = LOW_REPR_SAT2;
1608 }
1609 if(filePixelValueDbl > VALID_MAX2 + 0.5) {
1610 raw = HIGH_REPR_SAT2;
1611 }
1612 else {
1613 int filePixelValue = (int)round(filePixelValueDbl);
1614
1615 if(filePixelValue < VALID_MIN2) {
1616 raw = LOW_REPR_SAT2;
1617 }
1618 else if(filePixelValue > VALID_MAX2) {
1619 raw = HIGH_REPR_SAT2;
1620 }
1621 else {
1622 raw = filePixelValue;
1623 }
1624 }
1625 }
1626 else {
1627 if(bufferVal == NULL8)
1628 raw = NULL2;
1629 else if(bufferVal == LOW_INSTR_SAT8)
1630 raw = LOW_INSTR_SAT2;
1631 else if(bufferVal == LOW_REPR_SAT8)
1632 raw = LOW_REPR_SAT2;
1633 else if(bufferVal == HIGH_INSTR_SAT8)
1634 raw = HIGH_INSTR_SAT2;
1635 else if(bufferVal == HIGH_REPR_SAT8)
1636 raw = HIGH_REPR_SAT2;
1637 else
1638 raw = LOW_REPR_SAT2;
1639 }
1640
1641 ((short *)chunkBuf)[chunkIndex] =
1642 m_byteSwapper ? m_byteSwapper->ShortInt(&raw) : raw;
1643 }
1644
1645 else if(m_pixelType == UnsignedInteger) {
1646
1647 unsigned int raw;
1648
1649 if(bufferVal >= VALID_MINUI4) {
1650 double filePixelValueDbl = (bufferVal - m_base) /
1652 if(filePixelValueDbl < VALID_MINUI4 - 0.5) {
1653 raw = LOW_REPR_SATUI4;
1654 }
1655 if(filePixelValueDbl > VALID_MAXUI4) {
1656 raw = HIGH_REPR_SATUI4;
1657 }
1658 else {
1659 unsigned int filePixelValue = (unsigned int)round(filePixelValueDbl);
1660
1661 if(filePixelValue < VALID_MINUI4) {
1662 raw = LOW_REPR_SATUI4;
1663 }
1664 else if(filePixelValue > VALID_MAXUI4) {
1665 raw = HIGH_REPR_SATUI4;
1666 }
1667 else {
1668 raw = filePixelValue;
1669 }
1670 }
1671 }
1672 else {
1673 if(bufferVal == NULL8)
1674 raw = NULLUI4;
1675 else if(bufferVal == LOW_INSTR_SAT8)
1676 raw = LOW_INSTR_SATUI4;
1677 else if(bufferVal == LOW_REPR_SAT8)
1678 raw = LOW_REPR_SATUI4;
1679 else if(bufferVal == HIGH_INSTR_SAT8)
1680 raw = HIGH_INSTR_SATUI4;
1681 else if(bufferVal == HIGH_REPR_SAT8)
1682 raw = HIGH_REPR_SATUI4;
1683 else
1684 raw = LOW_REPR_SATUI4;
1685 }
1686
1687 ((unsigned int *)chunkBuf)[chunkIndex] =
1688 m_byteSwapper ? m_byteSwapper->Uint32_t(&raw) : raw;
1689
1690 }
1691
1692
1693 else if(m_pixelType == UnsignedWord) {
1694 unsigned short raw;
1695
1696 if(bufferVal >= VALID_MIN8) {
1697 double filePixelValueDbl = (bufferVal - m_base) /
1699 if(filePixelValueDbl < VALID_MINU2 - 0.5) {
1700 raw = LOW_REPR_SATU2;
1701 }
1702 if(filePixelValueDbl > VALID_MAXU2 + 0.5) {
1703 raw = HIGH_REPR_SATU2;
1704 }
1705 else {
1706 int filePixelValue = (int)round(filePixelValueDbl);
1707
1708 if(filePixelValue < VALID_MINU2) {
1709 raw = LOW_REPR_SATU2;
1710 }
1711 else if(filePixelValue > VALID_MAXU2) {
1712 raw = HIGH_REPR_SATU2;
1713 }
1714 else {
1715 raw = filePixelValue;
1716 }
1717 }
1718 }
1719 else {
1720 if(bufferVal == NULL8)
1721 raw = NULLU2;
1722 else if(bufferVal == LOW_INSTR_SAT8)
1723 raw = LOW_INSTR_SATU2;
1724 else if(bufferVal == LOW_REPR_SAT8)
1725 raw = LOW_REPR_SATU2;
1726 else if(bufferVal == HIGH_INSTR_SAT8)
1727 raw = HIGH_INSTR_SATU2;
1728 else if(bufferVal == HIGH_REPR_SAT8)
1729 raw = HIGH_REPR_SATU2;
1730 else
1731 raw = LOW_REPR_SATU2;
1732 }
1733
1734 ((unsigned short *)chunkBuf)[chunkIndex] =
1735 m_byteSwapper ? m_byteSwapper->UnsignedShortInt(&raw) : raw;
1736 }
1737 else if(m_pixelType == UnsignedByte) {
1738 unsigned char raw;
1739
1740 if(bufferVal >= VALID_MIN8) {
1741 double filePixelValueDbl = (bufferVal - m_base) /
1743 if(filePixelValueDbl < VALID_MIN1 - 0.5) {
1744 raw = LOW_REPR_SAT1;
1745 }
1746 else if(filePixelValueDbl > VALID_MAX1 + 0.5) {
1747 raw = HIGH_REPR_SAT1;
1748 }
1749 else {
1750 int filePixelValue = (int)(filePixelValueDbl + 0.5);
1751 if(filePixelValue < VALID_MIN1) {
1752 raw = LOW_REPR_SAT1;
1753 }
1754 else if(filePixelValue > VALID_MAX1) {
1755 raw = HIGH_REPR_SAT1;
1756 }
1757 else {
1758 raw = (unsigned char)(filePixelValue);
1759 }
1760 }
1761 }
1762 else {
1763 if(bufferVal == NULL8)
1764 raw = NULL1;
1765 else if(bufferVal == LOW_INSTR_SAT8)
1766 raw = LOW_INSTR_SAT1;
1767 else if(bufferVal == LOW_REPR_SAT8)
1768 raw = LOW_REPR_SAT1;
1769 else if(bufferVal == HIGH_INSTR_SAT8)
1770 raw = HIGH_INSTR_SAT1;
1771 else if(bufferVal == HIGH_REPR_SAT8)
1772 raw = HIGH_REPR_SAT1;
1773 else
1774 raw = LOW_REPR_SAT1;
1775 }
1776
1777 ((unsigned char *)chunkBuf)[chunkIndex] = raw;
1778 }
1779
1780 bufferIndex ++;
1781 }
1782 }
1783 }
1784 }
1785 }
1786
1787
1792 if(!m_dataIsOnDiskMap) {
1793 IString msg = "Cannot call CubeIoHandler::writeNullDataToDisk unless "
1794 "data is not already on disk (Cube::Create was called)";
1795 throw IException(IException::Programmer, msg, _FILEINFO_);
1796 }
1797
1798 int numChunks = getChunkCount();
1799 for(int i = 0; i < numChunks; i++) {
1800 if(!(*m_dataIsOnDiskMap)[i]) {
1801 RawCubeChunk *nullChunk = getNullChunk(i);
1802 (const_cast<CubeIoHandler *>(this))->writeRaw(*nullChunk);
1803 (*m_dataIsOnDiskMap)[i] = true;
1804
1805 delete nullChunk;
1806 nullChunk = NULL;
1807 }
1808 }
1809 }
1810
1811
1826 CubeIoHandler * ioHandler, QList<Buffer *> buffersToWrite) {
1827 m_ioHandler = ioHandler;
1828 m_buffersToWrite = new QList<Buffer *>(buffersToWrite);
1829 m_timer = new QElapsedTimer;
1830 m_timer->start();
1831
1832 m_ioHandler->m_writeThreadMutex->lock();
1833 }
1834
1835
1847 int elapsedMs = m_timer->elapsed();
1848 int idealFlushElapsedTime = 100; // ms
1849
1850 // We want to aim our flush size at 100ms, so adjust accordingly to aim at
1851 // our target. This method seems to be extremely effective because
1852 // we maximize our I/O calls when caching is interfering and normalize
1853 // them otherwise.
1854 int msOffIdeal = elapsedMs - idealFlushElapsedTime;
1855 double percentOffIdeal = msOffIdeal / (double)idealFlushElapsedTime;
1856
1857 // flush size is bounded to [32, 5000]
1858 int currentCacheSize = m_ioHandler->m_idealFlushSize;
1859 int desiredAdjustment = -1 * currentCacheSize * percentOffIdeal;
1860 int desiredCacheSize = (int)(currentCacheSize + desiredAdjustment);
1861 m_ioHandler->m_idealFlushSize = (int)(qMin(5000,
1862 qMax(32, desiredCacheSize)));
1863
1864 delete m_timer;
1865 m_timer = NULL;
1866
1867 m_ioHandler->m_writeThreadMutex->unlock();
1868 m_ioHandler = NULL;
1869
1870 delete m_buffersToWrite;
1871 m_buffersToWrite = NULL;
1872 }
1873
1874
1881 // Sorting the buffers didn't seem to have a large positive impact on speed,
1882 // but does increase complexity so it's disabled.
1883// QList<Buffer * > buffersToWrite(*m_buffersToWrite);
1884// std::sort(buffersToWrite.begin(), buffersToWrite.end(), bufferLessThan);
1885
1886 // If the buffers have any overlap at all then we can't sort them and still
1887 // guarantee the last write() call makes it into the correct place. The
1888 // bufferLessThan is guaranteed to return false if there is overlap.
1889// bool sortable = true;
1890// for (int i = 1; sortable && i < buffersToWrite.size(); i++) {
1891// if (!bufferLessThan(buffersToWrite[i - 1], buffersToWrite[i])) {
1892// sortable = false;
1893// }
1894// }
1895
1896// if (!sortable) {
1897// buffersToWrite = *m_buffersToWrite;
1898// }
1899
1900 foreach (Buffer * bufferToWrite, *m_buffersToWrite) {
1901 m_ioHandler->synchronousWrite(*bufferToWrite);
1902 delete bufferToWrite;
1903 }
1904
1905 m_buffersToWrite->clear();
1906 m_ioHandler->m_dataFile->flush();
1907 }
1908}
Represents a 3D area (a 3D "cube")
Definition Area3D.h:29
bool isValid() const
Returns true if all of the positions of the 3D area are valid (i.e.
Definition Area3D.cpp:489
Area3D intersect(const Area3D &otherArea) const
Returns the intersection of this 3D area with another 3D area.
Definition Area3D.cpp:462
Buffer for containing a three dimensional section of an image.
Definition Brick.h:45
void SetBasePosition(const int start_sample, const int start_line, const int start_band)
This method is used to set the base position of the shape buffer.
Definition Brick.h:120
Buffer for reading and writing cube data.
Definition Buffer.h:53
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:146
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition Buffer.h:138
void * RawBuffer() const
Returns a void pointer to the raw buffer.
Definition Buffer.h:151
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition Buffer.h:79
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition Buffer.cpp:163
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition Buffer.h:88
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition Buffer.h:70
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:198
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition Buffer.cpp:128
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.
QElapsedTimer * 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.
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.
virtual 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.
virtual 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.
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.
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.
virtual void readRaw(RawCubeChunk &chunkToFill)=0
This needs to populate the chunkToFill with unswapped raw bytes from the disk.
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...
virtual void writeRaw(const RawCubeChunk &chunkToWrite)=0
This needs to write the chunkToWrite directly to disk with no modifications to the data itself.
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.
virtual 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.
virtual void write(const Buffer &bufferToWrite)
Write buffer data into the cube data on disk.
BigInt m_startByte
The start byte of the cube data.
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
virtual 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
QList< int > * m_virtualBands
Converts from virtual band to physical band.
QMutex * m_writeThreadMutex
This enables us to block while the write thread is working.
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
bool isDirty() 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.
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.