81 {
82 if (_openSlideBase == null && _slideBase == null)
83 return;
84 if (viewportWidth <= 1 && viewportHeight <= 1)
85 return;
86 var schema = _useOpenSlide ? _openSlideBase.Schema : _slideBase.Schema;
87
88 int level = TileUtil.GetLevel(schema.Resolutions, resolution);
89 var levelRes = schema.Resolutions[level];
90 double levelUnitsPerPixel = levelRes.UnitsPerPixel;
91
92 int[]? requestedRgbSelection = rgbSelection?.ToArray();
93 if (rgbZarr != null && requestedRgbSelection == null)
94 requestedRgbSelection = new[] { 0, 1, 2 };
95 bool contentChanged = !_hasContentKey
96 || coordinate.Z != _contentZ
97 || coordinate.C != _contentC
98 || coordinate.T != _contentT
99 || !ReferenceEquals(rgbZarr, _contentRgbZarr)
100 || !SelectionsEqual(requestedRgbSelection, _contentRgbSelection);
101
102
103
104
105 int renderVersion = ++_renderVersion;
106 if (contentChanged || level != _currentLevel)
107 {
109 _uploadedTiles.Clear();
110 _currentLevel = level;
111 }
112
113 _hasContentKey = true;
114 _contentZ = coordinate.Z;
115 _contentC = coordinate.C;
116 _contentT = coordinate.T;
117 _contentRgbZarr = rgbZarr;
118 _contentRgbSelection = requestedRgbSelection;
119
120
121 double minX = pyramidalOrigin.X * resolution;
122 double minY = -pyramidalOrigin.Y * resolution;
123 double width = viewportWidth * resolution;
124 double height = viewportHeight * resolution;
125 var worldExtent = new Extent(minX, minY - height, minX + width, minY);
126
127
128 var tileInfos = schema.GetTileInfos(worldExtent, level).ToList();
129
130 if (tileInfos.Count == 0)
131 {
132 var pixelExtent = OpenSlideGTK.ExtentEx.WorldToPixelInvertedY(worldExtent, resolution);
133 tileInfos = schema.GetTileInfos(pixelExtent, level).ToList();
134 }
135
136 int[]? rgbChannels = requestedRgbSelection;
137
138
139
140 if (rgbZarr == null && !_useOpenSlide)
141 await _slideBase.FetchTilesAsync(tileInfos.ToList(), level, coordinate, pyramidalOrigin, new AForge.Size(viewportWidth, viewportHeight));
142 if (renderVersion != _renderVersion)
143 return;
144
145 var renderInfos = new List<TileRenderInfo>();
146 foreach (var tileInfo in tileInfos)
147 {
149 {
150 byte[] tileData;
151 if (rgbZarr != null)
152 {
153 int tileWidth = schema.GetTileWidth(level);
154 int tileHeight = schema.GetTileHeight(level);
155 int tilePixelWidth = (int)Math.Round(tileInfo.Extent.Width / levelUnitsPerPixel);
156 int tilePixelHeight = (int)Math.Round(tileInfo.Extent.Height / levelUnitsPerPixel);
157 tileData = await ZarrChannelView.ReadRgbTileAsync(rgbZarr, level,
158 tileInfo.Index.Col * tileWidth, tileInfo.Index.Row * tileHeight,
159 tilePixelWidth, tilePixelHeight, coordinate, rgbChannels!);
160 }
161 else if (_useOpenSlide)
162 tileData = await _openSlideBase.GetTileAsync(tileInfo);
163 else
164 tileData = await _slideBase.GetTileAsync(tileInfo, coordinate);
165 if (renderVersion != _renderVersion)
166 return;
167 if (tileData != null && rgbZarr == null && rgbChannels != null)
168 {
169 tileData = (byte[])tileData.Clone();
170 for (int i = 0; i + 3 < tileData.Length; i += 4)
171 for (int color = 0; color < 3; color++)
172 if (rgbChannels[color] < 0) tileData[i + color] = 0;
173 }
174 if (tileData != null)
175 {
176 int tW = (int)Math.Round(tileInfo.Extent.Width / levelUnitsPerPixel);
177 int tH = (int)Math.Round(tileInfo.Extent.Height / levelUnitsPerPixel);
178
179
180
181
182
183
184 if (rgbZarr == null)
185 {
186 int sourceWidth = schema.GetTileWidth(level);
187 int sourceHeight = schema.GetTileHeight(level);
188 if ((tW != sourceWidth || tH != sourceHeight) &&
189 tileData.Length >= sourceWidth * sourceHeight * 4)
190 {
191 byte[] clipped = new byte[tW * tH * 4];
192 int copyBytes = tW * 4;
193 for (int row = 0; row < tH; row++)
194 Buffer.BlockCopy(tileData, row * sourceWidth * 4,
195 clipped, row * copyBytes, copyBytes);
196 tileData = clipped;
197 }
198 }
199
201 _uploadedTiles.Add(tileInfo.Index);
202 }
203 }
204
206 {
207 var renderInfo = CalculateScreenPosition(
208 tileInfo,
209 pyramidalOrigin,
210 resolution,
211 level,
212 schema);
213 renderInfos.Add(renderInfo);
214 }
215 }
216
219 }
void RequestRedraw()
Request a redraw of the GLArea.
bool HasTileTexture(TileIndex index)
Check if a tile texture is already in the GPU cache.
bool UploadTileTexture(TileIndex index, byte[] pixelData, int tileWidth, int tileHeight)
Upload a tile texture to the GPU. Call from the main thread. Returns true if upload succeeded.
void SetTilesToRender(IEnumerable< TileRenderInfo > tiles)
Prepare tiles for rendering. Call before RequestRedraw().