3using Bitmap = AForge.Bitmap;
4using System.ComponentModel;
9 public partial class ImageView
12 private readonly PictureBox overviewPanel =
new PictureBox();
14 private BioImage previewRequestImage;
15 private (
int Z,
int C,
int T) previewRequestCoordinate;
16 private int previewVersion;
17 private bool overviewDragging;
18 private bool showOverview =
true;
20 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
27 UpdateOverviewVisibility();
31 private void InitializeOverview()
33 gl.Dock = DockStyle.Fill;
34 gl.ContextMenuStrip = contextMenuStrip;
35 contextMenuStrip.Opening += contextMenuStrip_Opening;
36 overviewPanel.Name =
"overviewPanel";
37 overviewPanel.BackColor =
Drawing.Color.Black;
38 overviewPanel.Cursor = Cursors.Cross;
39 overviewPanel.Visible =
false;
40 overviewPanel.ContextMenuStrip = contextMenuStrip;
41 overviewPanel.Paint += OverviewPaint;
42 overviewPanel.MouseDown += OverviewMouseDown;
43 overviewPanel.MouseMove += OverviewMouseMove;
44 overviewPanel.MouseUp += OverviewMouseUp;
45 overviewPanel.MouseWheel += ImageView_MouseWheel;
46 overviewPanel.MouseEnter += (_, _) => overviewPanel.Focus();
47 overviewPanel.MouseCaptureChanged += (_, _) =>
49 if (!overviewPanel.Capture)
50 overviewDragging =
false;
52 viewpanel.Controls.Add(overviewPanel);
61 private BioImage OverviewSelectedImage =>
64 private void UpdateOverviewVisibility()
66 if (overviewPanel.IsDisposed)
69 overviewSource == OverviewSelectedImage;
70 if (overviewPanel.Visible)
71 overviewPanel.BringToFront();
76 int width = image.isPyramidal ? image.Resolutions[0].SizeX : source.Width;
77 int height = image.isPyramidal ? image.Resolutions[0].SizeY : source.Height;
78 double ratio = 160.0 / Math.Max(width, height);
79 int previewWidth = Math.Max(1, (
int)Math.Round(width * ratio));
80 int previewHeight = Math.Max(1, (
int)Math.Round(height * ratio));
81 var thumbnail =
new Drawing.Bitmap(previewWidth, previewHeight);
82 using (var graphics =
Drawing.Graphics.FromImage(thumbnail))
84 graphics.InterpolationMode =
Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
85 graphics.DrawImage(source, 0, 0, previewWidth, previewHeight);
89 overviewSource = image;
90 overview =
new AForge.Rectangle(8, 8, previewWidth, previewHeight);
91 overviewPanel.SetBounds(overview.X, overview.Y, overview.Width, overview.Height);
92 UpdateOverviewVisibility();
93 overviewPanel.Invalidate();
96 private void UpdateRegularOverview()
98 BioImage image = OverviewSelectedImage;
99 if (image ==
null || image.isPyramidal ||
SelectedIndex >= Bitmaps.Count)
102 previewRequestImage =
null;
106 private async
void InitPreview()
108 BioImage image = OverviewSelectedImage;
109 if (image ==
null || !image.isPyramidal)
111 var coordinate = (image.Coordinate.Z, image.Coordinate.C, image.Coordinate.T);
112 if (previewRequestImage == image && previewRequestCoordinate == coordinate)
114 previewRequestImage = image;
115 previewRequestCoordinate = coordinate;
116 int version = ++previewVersion;
117 UpdateOverviewVisibility();
120 if (image.OpenSlideBase !=
null)
124 var slide = image.OpenSlideBase;
125 var levels = slide.Schema.Resolutions.OrderBy(r => r.Value.UnitsPerPixel).ToArray();
126 var levelInfo = levels.Where(r =>
127 Math.Max(slide.Schema.Extent.Width, slide.Schema.Extent.Height) /
128 r.Value.UnitsPerPixel >= 160).DefaultIfEmpty(levels[0]).Last();
129 int width = (int)Math.Ceiling(slide.Schema.Extent.Width / levelInfo.Value.UnitsPerPixel);
130 int height = (int)Math.Ceiling(slide.Schema.Extent.Height / levelInfo.Value.UnitsPerPixel);
131 if ((
long)width * height > 16_000_000)
133 using Drawing.Bitmap slidePreview = await Task.Run(() => SlideOverviewBuilder.Build(width, height,
134 levelInfo.Value.TileWidth, levelInfo.Value.TileHeight,
135 (column, row) => slide.GetTile(
new BruTile.TileInfo
137 Index = new BruTile.TileIndex(column, row, levelInfo.Key)
139 if (version == previewVersion && !IsDisposed && !Disposing && image == OverviewSelectedImage)
140 SetOverviewPreview(slidePreview, image);
144 int level = GetPreviewLevel(image);
145 var resolution = image.Resolutions[level];
146 if ((
long)resolution.SizeX * resolution.SizeY > 16_000_000)
148 int frame = image.GetFrameIndex(coordinate.Z, coordinate.C, coordinate.T);
149 using Bitmap tile = await image.GetTile(frame, level, 0, 0, resolution.SizeX, resolution.SizeY,
150 new ZCT(coordinate.Item1, coordinate.Item2, coordinate.Item3),
true);
151 if (tile ==
null || version != previewVersion || IsDisposed || Disposing ||
152 image != OverviewSelectedImage)
154 using Drawing.Bitmap display = ToSysBitmap(tile);
155 SetOverviewPreview(display, image);
159 System.Diagnostics.Trace.WriteLine($
"Could not load slide overview: {ex.Message}");
165 if (image.isPyramidal)
169 var extent = image.OpenSlideBase?.Schema.Extent ?? image.SlideBase?.Schema.Extent;
170 if (extent is { } schemaExtent)
171 return new RectangleD(schemaExtent.MinX, -schemaExtent.MaxY, schemaExtent.Width, schemaExtent.Height);
172 double units = image.GetUnitPerPixel(0);
173 return new RectangleD(0, 0, image.Resolutions[0].SizeX * units,
174 image.Resolutions[0].SizeY * units);
176 return new RectangleD(image.Volume.Location.X, image.Volume.Location.Y,
177 image.Volume.Width, image.Volume.Height);
180 private void OverviewPaint(
object sender, PaintEventArgs e)
182 BioImage image = OverviewSelectedImage;
183 if (
overviewImage ==
null || image ==
null || overviewSource != image)
186 RectangleD bounds = OverviewWorldBounds(image);
187 if (bounds.W <= 0 || bounds.H <= 0)
190 PointD topLeft = image.isPyramidal
191 ?
new PointD(image.PyramidalOrigin.X, image.PyramidalOrigin.Y)
196 double left = Math.Clamp((topLeft.X - bounds.X) / bounds.W, 0, 1);
197 double top = Math.Clamp((topLeft.Y - bounds.Y) / bounds.H, 0, 1);
198 double right = Math.Clamp((topLeft.X + viewWidth - bounds.X) / bounds.W, 0, 1);
199 double bottom = Math.Clamp((topLeft.Y + viewHeight - bounds.Y) / bounds.H, 0, 1);
200 if (right > left && bottom > top)
203 e.Graphics.DrawRectangle(pen, (
float)(left * (overview.Width - 1)),
204 (
float)(top * (overview.Height - 1)), (
float)((right - left) * (overview.Width - 1)),
205 (
float)((bottom - top) * (overview.Height - 1)));
207 e.Graphics.DrawRectangle(
Drawing.Pens.White, 0, 0, overview.Width - 1, overview.Height - 1);
210 private void NavigateOverview(
int x,
int y)
212 BioImage image = OverviewSelectedImage;
213 if (image ==
null ||
overviewImage ==
null || overviewSource != image)
216 RectangleD bounds = OverviewWorldBounds(image);
217 double targetX = bounds.X + Math.Clamp(x / (
double)overview.Width, 0, 1) * bounds.W;
218 double targetY = bounds.Y + Math.Clamp(y / (
double)overview.Height, 0, 1) * bounds.H;
219 if (image.isPyramidal)
230 private MouseEventArgs OverviewCanvasEvent(MouseEventArgs e) =>
231 new MouseEventArgs(e.Button, e.Clicks, e.X + overview.X, e.Y + overview.Y, e.Delta);
233 private void OverviewMouseDown(
object sender, MouseEventArgs e)
235 if (e.Button == MouseButtons.Left)
237 overviewDragging =
true;
238 overviewPanel.Capture =
true;
239 NavigateOverview(e.X, e.Y);
241 else if (e.Button == MouseButtons.Middle)
242 pictureBox_MouseDown(sender, OverviewCanvasEvent(e));
245 private void OverviewMouseMove(
object sender, MouseEventArgs e)
247 if (overviewDragging && e.Button.HasFlag(MouseButtons.Left))
248 NavigateOverview(e.X, e.Y);
249 else if (e.Button == MouseButtons.Middle)
250 rgbPictureBox_MouseMove(sender, OverviewCanvasEvent(e));
253 private void OverviewMouseUp(
object sender, MouseEventArgs e)
255 if (e.Button == MouseButtons.Left)
257 overviewDragging =
false;
258 overviewPanel.Capture =
false;
260 else if (e.Button == MouseButtons.Middle)
261 pictureBox_MouseUp(sender, OverviewCanvasEvent(e));
AForge.RectangleD RectangleD
new AForge.SizeF Scale
Scale when opening image stacks.
PointD Origin
A property of the class PointD. */.
PointD PyramidalOrigin
Setting the value of the pyramidalOrigin variable. */.
void UpdateView(bool refreshImages=true)
double PxHmicron
Only used when opening stacks.
double PxWmicron
Only used when opening stacks.
int SelectedIndex
A property that is used to set the selected index of the image. */.
async void UpdateImages(bool updatePyramidal=true)
double Resolution
A property of the class ImageViewer. It is a getter and setter for the resolution of the image.
Drawing.Bitmap overviewImage