96 {
97 _pendingPyramidalOrigin = pyramidalOrigin;
98 _pendingViewportWidth = viewportWidth;
99 _pendingViewportHeight = viewportHeight;
100 _pendingResolution = resolution;
101 _pendingCoordinate = coordinate;
102 _pendingRgbZarr = rgbZarr;
103 _pendingRgbSelection = rgbSelection?.ToArray();
104 _hasPendingView = true;
105
106 if (_openSlideBase == null && _slideBase == null)
107 return;
108 if (viewportWidth <= 1 || viewportHeight <= 1 || resolution <= 0 || !double.IsFinite(resolution))
109 return;
111 return;
112 if (!_renderSemaphore.Wait(0))
113 {
114 return;
115 }
116
117 try
118 {
119 var schema = _useOpenSlide ? _openSlideBase.Schema : _slideBase.Schema;
120 int level = TileUtil.GetLevel(schema.Resolutions, resolution);
121 var levelRes = schema.Resolutions[level];
122 double levelUnitsPerPixel = levelRes.UnitsPerPixel;
123
124 var contentCoordinate = (coordinate.Z, coordinate.C, coordinate.T);
125 if (_contentCoordinate != contentCoordinate || !ReferenceEquals(_rgbZarr, rgbZarr) ||
126 !SelectionsEqual(_rgbSelection, rgbSelection) || level != _currentLevel)
127 {
128
130 _contentCoordinate = contentCoordinate;
131 _rgbZarr = rgbZarr;
132 _rgbSelection = rgbSelection?.ToArray();
133 _currentLevel = level;
134 }
135 int contentVersion = _contentVersion;
136
137 double minX = pyramidalOrigin.X;
138 double width = viewportWidth * resolution;
139 double height = viewportHeight * resolution;
140 var viewportExtent = new Extent(minX, -pyramidalOrigin.Y - height, minX + width, -pyramidalOrigin.Y);
141
142 double tileMarginX = levelRes.TileWidth * levelUnitsPerPixel;
143 double tileMarginY = levelRes.TileHeight * levelUnitsPerPixel;
144 var fetchExtent = new Extent(
145 viewportExtent.MinX - tileMarginX,
146 viewportExtent.MinY - tileMarginY,
147 viewportExtent.MaxX + tileMarginX,
148 viewportExtent.MaxY + tileMarginY);
149
150 var tileInfos = schema.GetTileInfos(fetchExtent, level).ToList();
151 if (tileInfos.Count == 0)
152 {
153 var lookupExtent = BioLib.ExtentEx.WorldToPixelInvertedY(fetchExtent, levelUnitsPerPixel);
154 tileInfos = schema.GetTileInfos(lookupExtent, level).ToList();
155 }
156 var renderInfos = new List<TileRenderInfo>();
157 var pendingFetches = new List<(TileInfo Tile, Task<byte[]> FetchTask)>();
158 foreach (var tileInfo in tileInfos)
159 {
161 {
162 renderInfos.Add(CalculateScreenPosition(
163 tileInfo,
164 pyramidalOrigin,
165 resolution,
166 level,
167 schema));
168 continue;
169 }
170
171 bool alreadyPending;
172 lock (_pendingTileFetchLock)
173 {
174 alreadyPending = !_pendingTileFetches.Add((tileInfo.Index, contentVersion));
175 }
176
177 if (!alreadyPending)
178 {
179 pendingFetches.Add((tileInfo, FetchTileAsync(tileInfo, level, coordinate, schema, rgbZarr, rgbSelection?.ToArray(), RemoteTileFetchTimeout)));
180 }
181 renderInfos.Add(CalculateScreenPosition(
182 tileInfo, pyramidalOrigin, resolution, level, schema));
183 }
184
185 foreach (var pending in pendingFetches)
186 {
187 _ = ProcessPendingFetchAsync(
188 pending.Tile,
189 pending.FetchTask,
190 pyramidalOrigin,
191 resolution,
192 level,
193 schema,
194 contentVersion,
195 rgbZarr != null);
196 }
197
200 }
201 finally
202 {
203 _renderSemaphore.Release();
204 }
205 }
bool IsGLReady
True after the GL context has been fully initialized and is ready for texture uploads and rendering.
void SetTilesToRender(IEnumerable< TileRenderInfo > tiles)
Prepare tiles for rendering. Call before RequestRedraw().
void RequestRedraw()
Request a redraw of the GLArea.
bool HasTileTexture(TileIndex index)
Check if a tile texture is already in the GPU cache.