@@ -76,15 +76,94 @@ void ImagePyramidRenderer::loadAttributes() {
7676 setSharpening (getBooleanAttribute (" sharpening" ));
7777}
7878
79- void
80- ImagePyramidRenderer::draw (Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D,
81- int viewWidth,
82- int viewHeight) {
79+ int ImagePyramidRenderer::loadTileTexture (std::string tileID) {
80+ // Check if tile has been processed before
81+ if (mTexturesToRender .count (tileID) > 0 )
82+ return 0 ;
83+
84+ // std::cout << "Loading tile " << tileID << " queue size: " << m_tileQueue.size() << std::endl;
85+
86+ // Create texture
87+ auto parts = split (tileID, " _" );
88+ if (parts.size () != 3 )
89+ throw Exception (" incorrect tile format" );
90+
91+ int level = std::stoi (parts[0 ]);
92+ int tile_x = std::stoi (parts[1 ]);
93+ int tile_y = std::stoi (parts[2 ]);
94+ // std::cout << "Creating texture for tile " << tile_x << " " << tile_y << " at level " << level << std::endl;
95+ Image::pointer tile;
96+ {
97+ auto access = m_input->getAccess (ACCESS_READ);
98+ try {
99+ tile = access->getPatchAsImage (level, tile_x, tile_y, false );
100+ } catch (Exception &e) {
101+ // reportWarning() << "Error occured while trying to open patch " << tile_x << " " << tile_y << reportEnd();
102+ // Tile was missing, just skip it..
103+ std::lock_guard<std::mutex> lock (m_tileQueueMutex);
104+ mTexturesToRender [tileID] = 0 ;
105+ return 0 ;
106+ }
107+ if (m_postProcessingSharpening) {
108+ m_sharpening->setInputData (tile);
109+ tile = m_sharpening->updateAndGetOutputData <Image>();
110+ }
111+ }
112+ auto tileAccess = tile->getImageAccess (ACCESS_READ);
113+ // Copy data from CPU to GL texture
114+ GLuint textureID;
115+ glGenTextures (1 , &textureID);
116+ glBindTexture (GL_TEXTURE_2D, textureID);
117+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
118+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
119+
120+ // TODO Why is this needed:
121+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
122+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
123+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
124+
125+ if (tile->getWidth ()*tile->getNrOfChannels () % 4 != 0 ) {
126+ // By default OpenGL assumes that the start of each row of an image is aligned 4 bytes.
127+ // Thus we have to change this here
128+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1 ); // Fix alignment issues
129+ }
130+
131+ // WSI data from openslide is stored as ARGB, need to handle this here: BGRA and reverse
132+ if (m_input->isBGRA ()) {
133+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGBA, tile->getWidth (), tile->getHeight (), 0 , GL_BGRA,
134+ GL_UNSIGNED_BYTE,
135+ tileAccess->get ());
136+ } else {
137+ if (tile->getNrOfChannels () == 3 ) {
138+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGB, tile->getWidth (), tile->getHeight (), 0 , GL_RGB,
139+ GL_UNSIGNED_BYTE,
140+ tileAccess->get ());
141+ } else if (tile->getNrOfChannels () == 4 ) {
142+ glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGBA, tile->getWidth (), tile->getHeight (), 0 , GL_RGBA,
143+ GL_UNSIGNED_BYTE,
144+ tileAccess->get ());
145+ }
146+ }
147+ GLint compressedImageSize = 0 ;
148+ glGetTexLevelParameteriv (GL_TEXTURE_2D, 0 , GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedImageSize);
149+ glBindTexture (GL_TEXTURE_2D, 0 );
150+ glFinish (); // Make sure texture is done before adding it
151+
152+ {
153+ std::lock_guard<std::mutex> lock (m_tileQueueMutex);
154+ mTexturesToRender [tileID] = textureID;
155+ }
156+ return compressedImageSize;
157+ }
158+
159+ void ImagePyramidRenderer::draw (Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, float zNear, float zFar, bool mode2D,
160+ int viewWidth,
161+ int viewHeight) {
83162 auto dataToRender = getDataToRender ();
84163 if (dataToRender.empty ())
85164 return ;
86165
87- if (!m_bufferThread) {
166+ if (!m_bufferThread && m_view != nullptr ) {
88167 // Create thread to load patches
89168 // Create a GL context for the thread which is sharing with the context of the view
90169 auto context = new QGLContext (View::getGLFormat (), m_view);
@@ -139,83 +218,7 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
139218 m_tileQueue.pop_back ();
140219 }
141220
142- // Check if tile has been processed before
143- if (mTexturesToRender .count (tileID) > 0 )
144- continue ;
145-
146- // std::cout << "Loading tile " << tileID << " queue size: " << m_tileQueue.size() << std::endl;
147-
148- // Create texture
149- auto parts = split (tileID, " _" );
150- if (parts.size () != 3 )
151- throw Exception (" incorrect tile format" );
152-
153- int level = std::stoi (parts[0 ]);
154- int tile_x = std::stoi (parts[1 ]);
155- int tile_y = std::stoi (parts[2 ]);
156- // std::cout << "Creating texture for tile " << tile_x << " " << tile_y << " at level " << level << std::endl;
157- Image::pointer tile;
158- {
159- auto access = m_input->getAccess (ACCESS_READ);
160- try {
161- tile = access->getPatchAsImage (level, tile_x, tile_y, false );
162- } catch (Exception &e) {
163- // reportWarning() << "Error occured while trying to open patch " << tile_x << " " << tile_y << reportEnd();
164- // Tile was missing, just skip it..
165- std::lock_guard<std::mutex> lock (m_tileQueueMutex);
166- mTexturesToRender [tileID] = 0 ;
167- continue ;
168- }
169- if (m_postProcessingSharpening) {
170- m_sharpening->setInputData (tile);
171- tile = m_sharpening->updateAndGetOutputData <Image>();
172- }
173- }
174- auto tileAccess = tile->getImageAccess (ACCESS_READ);
175- // Copy data from CPU to GL texture
176- GLuint textureID;
177- glGenTextures (1 , &textureID);
178- glBindTexture (GL_TEXTURE_2D, textureID);
179- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
180- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
181-
182- // TODO Why is this needed:
183- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
184- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
185- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
186-
187- if (tile->getWidth ()*tile->getNrOfChannels () % 4 != 0 ) {
188- // By default OpenGL assumes that the start of each row of an image is aligned 4 bytes.
189- // Thus we have to change this here
190- glPixelStorei (GL_UNPACK_ALIGNMENT, 1 ); // Fix alignment issues
191- }
192-
193- // WSI data from openslide is stored as ARGB, need to handle this here: BGRA and reverse
194- if (m_input->isBGRA ()) {
195- glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGBA, tile->getWidth (), tile->getHeight (), 0 , GL_BGRA,
196- GL_UNSIGNED_BYTE,
197- tileAccess->get ());
198- } else {
199- if (tile->getNrOfChannels () == 3 ) {
200- glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGB, tile->getWidth (), tile->getHeight (), 0 , GL_RGB,
201- GL_UNSIGNED_BYTE,
202- tileAccess->get ());
203- } else if (tile->getNrOfChannels () == 4 ) {
204- glTexImage2D (GL_TEXTURE_2D, 0 , GL_COMPRESSED_RGBA, tile->getWidth (), tile->getHeight (), 0 , GL_RGBA,
205- GL_UNSIGNED_BYTE,
206- tileAccess->get ());
207- }
208- }
209- GLint compressedImageSize = 0 ;
210- glGetTexLevelParameteriv (GL_TEXTURE_2D, 0 , GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedImageSize);
211- glBindTexture (GL_TEXTURE_2D, 0 );
212- glFinish (); // Make sure texture is done before adding it
213-
214- {
215- std::lock_guard<std::mutex> lock (m_tileQueueMutex);
216- mTexturesToRender [tileID] = textureID;
217- }
218- memoryUsage += compressedImageSize;
221+ memoryUsage += loadTileTexture (tileID);
219222 // std::cout << "Texture cache in ImagePyramidRenderer using " << (float)memoryUsage / (1024 * 1024) << " MB" << std::endl;
220223 }
221224 });
@@ -257,7 +260,7 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
257260 float percentageShownX = (float )width / fullWidth;
258261 float percentageShownY = (float )height / fullHeight;
259262 // With current level, do we have have enough pixels to fill the view?
260- if (percentageShownX * levelWidth > m_view-> width () && percentageShownY * levelHeight > m_view-> height () ) {
263+ if (percentageShownX * levelWidth > viewWidth && percentageShownY * levelHeight > viewHeight ) {
261264 // If yes, stop here
262265 levelToUse = level;
263266 break ;
@@ -349,26 +352,30 @@ ImagePyramidRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix, f
349352 ))
350353 continue ;
351354
352-
353- // Is patch in cache?
354- bool textureReady = false ;
355355 uint textureID;
356- {
357- std::lock_guard<std::mutex> lock (m_tileQueueMutex);
358- textureReady = mTexturesToRender .count (tileString) > 0 ;
359- }
360- if (!textureReady) {
361- // Add to queue if not in cache
356+ if (m_view != nullptr ) {
357+ // Is patch in cache?
358+ bool textureReady = false ;
362359 {
363360 std::lock_guard<std::mutex> lock (m_tileQueueMutex);
364- // Remove any duplicates first
365- m_tileQueue.remove (tileString); // O(n) time complexity..
366- m_tileQueue.push_back (tileString);
367- // std::cout << "Added tile " << tileString << " to queue" << std::endl;
361+ textureReady = mTexturesToRender .count (tileString) > 0 ;
362+ }
363+ if (!textureReady) {
364+ // Add to queue if not in cache
365+ {
366+ std::lock_guard<std::mutex> lock (m_tileQueueMutex);
367+ // Remove any duplicates first
368+ m_tileQueue.remove (tileString); // O(n) time complexity..
369+ m_tileQueue.push_back (tileString);
370+ // std::cout << "Added tile " << tileString << " to queue" << std::endl;
371+ }
372+ m_queueEmptyCondition.notify_one ();
373+ continue ;
374+ } else {
375+ textureID = mTexturesToRender [tileString];
368376 }
369- m_queueEmptyCondition.notify_one ();
370- continue ;
371377 } else {
378+ int bytes = loadTileTexture (tileString);
372379 textureID = mTexturesToRender [tileString];
373380 }
374381
0 commit comments