BioGTK  6.7.0
A .NET library & program for annotating, editing various microscopy imaging formats using Bioformats supported images. Including whole slide, pyramidal, and series.
Loading...
Searching...
No Matches
BioGTK.SlideGLArea Class Reference

A GTK GLArea widget that renders pyramidal slide tiles directly on the GPU. Eliminates ReadPixels overhead by rendering to screen instead of copying to CPU. Supports SkiaSharp overlay for annotations via GRContext. More...

Inheritance diagram for BioGTK.SlideGLArea:

Public Member Functions

void DiscardCompleteFrame ()
 
void UploadTileTexture (TileIndex index, byte[] pixelData, int tileWidth, int tileHeight)
 Upload a tile texture to the GPU. Call from the main thread.
 
void GetUploadedTileSize (TileIndex index, out int w, out int h)
 Get the uploaded texture dimensions for a cached tile. Returns (0,0) if the tile is not in the cache.
 
bool HasTileTexture (TileIndex index)
 Check if a tile texture is already in the GPU cache.
 
void ReleaseTileTexture (TileIndex index)
 Release a specific tile texture.
 
void ReleaseLevelTextures (int level)
 Release all textures for a specific pyramid level.
 
void ClearTextureCache ()
 Clear all cached textures.
 
void RequestRedraw ()
 Request a redraw of the GLArea.
 
void CaptureNextWholeImageFrame (Action< byte[], int, int > callback)
 Captures the next complete fit-to-window image frame. A newer request replaces an older one because only the current image/coordinate matters.
 
void CancelWholeImageFrameCapture (Action< byte[], int, int > callback)
 
void SetTilesToRender (IEnumerable< TileRenderInfo > tiles)
 Prepare tiles for rendering. Call before RequestRedraw().
 
void SetProtectedTileIndices (IEnumerable< TileIndex > indices)
 Protects incoming visible tiles from FIFO eviction. The currently displayed set is included automatically so progressive level changes retain both the fallback frame and the target frame under construction.
 
byte[] ReadPixels ()
 Read pixels from the current framebuffer (for export/save operations). This is slow - only use for export, not display.
 

Properties

List< TileRenderInfo > TilesToRender = new() [get]
 
bool NeedsRedraw = true [get, set]
 
bool PreservePreviousFrameWhileIncomplete = false [get, set]
 
float ImageScreenX = -1 [get, set]
 
float ImageScreenY = -1 [get, set]
 
float ImageScreenW = -1 [get, set]
 
float ImageScreenH = -1 [get, set]
 
int CachedTextureCount [get]
 

Events

Action< SKCanvas, int, int > OnSkiaRender
 Event fired during Skia rendering phase for annotation drawing.
 

Detailed Description

A GTK GLArea widget that renders pyramidal slide tiles directly on the GPU. Eliminates ReadPixels overhead by rendering to screen instead of copying to CPU. Supports SkiaSharp overlay for annotations via GRContext.

Definition at line 19 of file SlideGLArea.cs.

Constructor & Destructor Documentation

◆ SlideGLArea()

BioGTK.SlideGLArea.SlideGLArea ( )

Definition at line 141 of file SlideGLArea.cs.

142 {
143 HasDepthBuffer = false;
144 HasStencilBuffer = false;
145 // Skia wraps this framebuffer as GL_RGBA8, so GTK must allocate an
146 // alpha-capable color attachment too. The final render pass explicitly
147 // writes alpha=1 across the surface, keeping presentation opaque while
148 // avoiding the undefined RGB-buffer-as-RGBA mismatch.
149 HasAlpha = true;
150 AutoRender = false; // We control when to render
151
152 Realized += OnRealized;
153 //Unrealize += OnUnrealize;
154 Render += OnRender;
155 Resize += OnResized;
156 }

Member Function Documentation

◆ CancelWholeImageFrameCapture()

void BioGTK.SlideGLArea.CancelWholeImageFrameCapture ( Action< byte[], int, int > callback)

Definition at line 874 of file SlideGLArea.cs.

875 {
876 if (ReferenceEquals(_pendingWholeImageCapture, callback))
877 _pendingWholeImageCapture = null;
878 }

◆ CaptureNextWholeImageFrame()

void BioGTK.SlideGLArea.CaptureNextWholeImageFrame ( Action< byte[], int, int > callback)

Captures the next complete fit-to-window image frame. A newer request replaces an older one because only the current image/coordinate matters.

Definition at line 868 of file SlideGLArea.cs.

869 {
870 _pendingWholeImageCapture = callback;
872 }
void RequestRedraw()
Request a redraw of the GLArea.

◆ ClearTextureCache()

void BioGTK.SlideGLArea.ClearTextureCache ( )

Clear all cached textures.

Definition at line 804 of file SlideGLArea.cs.

805 {
806 if (!_glInitialized) return;
807
808 MakeCurrent();
809
810 foreach (var tex in _textureCache.Values)
811 {
812 GL.DeleteTexture(tex);
813 }
814 _textureCache.Clear();
815 _textureSizes.Clear();
816 _protectedTileIndices.Clear();
817 _textureBytes = 0;
818 }

◆ DiscardCompleteFrame()

void BioGTK.SlideGLArea.DiscardCompleteFrame ( )

Definition at line 562 of file SlideGLArea.cs.

563 {
564 if (_completeFrameTexture != 0 && _glInitialized)
565 {
566 MakeCurrent();
567 GL.DeleteTexture(_completeFrameTexture);
568 }
569 _completeFrameTexture = 0;
570 _completeFrameWidth = 0;
571 _completeFrameHeight = 0;
572 }

◆ GetUploadedTileSize()

void BioGTK.SlideGLArea.GetUploadedTileSize ( TileIndex index,
out int w,
out int h )

Get the uploaded texture dimensions for a cached tile. Returns (0,0) if the tile is not in the cache.

Definition at line 745 of file SlideGLArea.cs.

746 {
747 if (_textureSizes.TryGetValue(index, out var sz))
748 { w = sz.w; h = sz.h; }
749 else
750 { w = 0; h = 0; }
751 }

◆ HasTileTexture()

bool BioGTK.SlideGLArea.HasTileTexture ( TileIndex index)

Check if a tile texture is already in the GPU cache.

Definition at line 756 of file SlideGLArea.cs.

757 {
758 return _textureCache.ContainsKey(index);
759 }

◆ ReadPixels()

byte[] BioGTK.SlideGLArea.ReadPixels ( )

Read pixels from the current framebuffer (for export/save operations). This is slow - only use for export, not display.

Definition at line 910 of file SlideGLArea.cs.

911 {
912 if (!_glInitialized) return null;
913
914 MakeCurrent();
915
916 int width = AllocatedWidth;
917 int height = AllocatedHeight;
918
919 byte[] pixels = new byte[width * height * 4];
920 GL.ReadPixels(0, 0, width, height, PixelFormat.Rgba, PixelType.UnsignedByte, pixels);
921
922 return pixels;
923 }

◆ ReleaseLevelTextures()

void BioGTK.SlideGLArea.ReleaseLevelTextures ( int level)

Release all textures for a specific pyramid level.

Definition at line 782 of file SlideGLArea.cs.

783 {
784 if (!_glInitialized) return;
785
786 MakeCurrent();
787
788 var toRemove = _textureCache.Where(kvp => kvp.Key.Level == level).ToList();
789 foreach (var kvp in toRemove)
790 {
791 GL.DeleteTexture(kvp.Value);
792 if (_textureSizes.TryGetValue(kvp.Key, out var sz))
793 {
794 _textureBytes -= (long)sz.w * sz.h * 4;
795 _textureSizes.Remove(kvp.Key);
796 }
797 _textureCache.Remove(kvp.Key);
798 }
799 }

◆ ReleaseTileTexture()

void BioGTK.SlideGLArea.ReleaseTileTexture ( TileIndex index)

Release a specific tile texture.

Definition at line 764 of file SlideGLArea.cs.

765 {
766 if (_textureCache.TryGetValue(index, out int tex))
767 {
768 MakeCurrent();
769 GL.DeleteTexture(tex);
770 _textureCache.Remove(index);
771 if (_textureSizes.TryGetValue(index, out var sz))
772 {
773 _textureBytes -= (long)sz.w * sz.h * 4;
774 _textureSizes.Remove(index);
775 }
776 }
777 }

◆ RequestRedraw()

void BioGTK.SlideGLArea.RequestRedraw ( )

Request a redraw of the GLArea.

Definition at line 857 of file SlideGLArea.cs.

858 {
859 NeedsRedraw = true;
860 QueueRender();
861 QueueDraw();
862 }

◆ SetProtectedTileIndices()

void BioGTK.SlideGLArea.SetProtectedTileIndices ( IEnumerable< TileIndex > indices)

Protects incoming visible tiles from FIFO eviction. The currently displayed set is included automatically so progressive level changes retain both the fallback frame and the target frame under construction.

Definition at line 895 of file SlideGLArea.cs.

896 {
897 _protectedTileIndices.Clear();
898 foreach (var index in indices)
899 _protectedTileIndices.Add(index);
900 foreach (var tile in TilesToRender)
901 _protectedTileIndices.Add(tile.Index);
902 }

◆ SetTilesToRender()

void BioGTK.SlideGLArea.SetTilesToRender ( IEnumerable< TileRenderInfo > tiles)

Prepare tiles for rendering. Call before RequestRedraw().

Definition at line 883 of file SlideGLArea.cs.

884 {
885 TilesToRender.Clear();
886 TilesToRender.AddRange(tiles);
887 LogDiag($"[SetTilesToRender] count={TilesToRender.Count}");
888 }

◆ UploadTileTexture()

void BioGTK.SlideGLArea.UploadTileTexture ( TileIndex index,
byte[] pixelData,
int tileWidth,
int tileHeight )

Upload a tile texture to the GPU. Call from the main thread.

Definition at line 650 of file SlideGLArea.cs.

651 {
652 if (!_glInitialized)
653 {
654 LogDiag($"[UploadTileTexture] gl-not-initialized tile={index}");
655 return;
656 }
657
658 if (pixelData == null || pixelData.Length == 0)
659 {
660 LogDiag($"[UploadTileTexture] empty pixel data tile={index}");
661 return;
662 }
663
664 // The buffer is always BGRA = 4 bytes per pixel.
665 // GetTile pads every tile — including edge tiles — to exactly
666 // tileWidth x tileHeight pixels, so the dimensions passed by SlideRenderer
667 // (the schema tile size) always match the buffer. We just validate and bail
668 // on any genuine mismatch rather than trying to guess new dimensions.
669 if (tileWidth <= 0 || tileHeight <= 0)
670 {
671 LogDiag($"[UploadTileTexture] invalid dimensions tile={index} size={tileWidth}x{tileHeight}");
672 return;
673 }
674
675 int expectedBytes = tileWidth * tileHeight * 4;
676 if (pixelData.Length < expectedBytes)
677 {
678 LogDiag($"[UploadTileTexture] buffer too small tile={index} need={expectedBytes} got={pixelData.Length}");
679 return;
680 }
681
682 MakeCurrent();
683
684 // Check if already cached
685 if (_textureCache.ContainsKey(index))
686 {
687 LogDiag($"[UploadTileTexture] already cached tile={index}");
688 return;
689 }
690
691 long newTextureBytes = (long)tileWidth * tileHeight * 4;
692
693 // Evict old textures if the cache is full or if the new upload would
694 // push the total texture budget over its byte cap.
695 while (_textureCache.Count > 0 &&
696 (_textureCache.Count >= MAX_CACHED_TEXTURES ||
697 _textureBytes + newTextureBytes > MAX_CACHED_TEXTURE_BYTES))
698 {
699 EvictOldestTextures(1);
700 }
701
702 // Create and upload texture.
703 // Pin the managed array explicitly so the GC cannot relocate it while the
704 // driver is reading from the pointer (avoids ExecutionEngineException).
705 // PixelFormat.Bgra matches the byte order produced by ReadRegionAsync.
706 int tex = GL.GenTexture();
707 if (tex == 0)
708 {
709 Console.WriteLine($"OpenGL failed to allocate texture for tile {index}.");
710 return;
711 }
712 GL.BindTexture(TextureTarget.Texture2D, tex);
713 GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
714
715 var handle = System.Runtime.InteropServices.GCHandle.Alloc(pixelData, System.Runtime.InteropServices.GCHandleType.Pinned);
716 try
717 {
718 GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba,
719 tileWidth, tileHeight, 0,
720 PixelFormat.Bgra, PixelType.UnsignedByte,
721 handle.AddrOfPinnedObject());
722 }
723 finally
724 {
725 handle.Free();
726 }
727
728 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
729 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
730 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
731 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
732
733 GL.BindTexture(TextureTarget.Texture2D, 0);
734
735 _textureCache[index] = tex;
736 _textureSizes[index] = (tileWidth, tileHeight);
737 _textureBytes += newTextureBytes;
738 LogDiag($"[UploadTileTexture] uploaded tile={index} tex={tex} size={tileWidth}x{tileHeight} bytes={pixelData.Length}");
739 }

Property Documentation

◆ CachedTextureCount

int BioGTK.SlideGLArea.CachedTextureCount
get

Definition at line 904 of file SlideGLArea.cs.

◆ ImageScreenH

float BioGTK.SlideGLArea.ImageScreenH = -1
getset

Definition at line 78 of file SlideGLArea.cs.

78{ get; set; } = -1;

◆ ImageScreenW

float BioGTK.SlideGLArea.ImageScreenW = -1
getset

Definition at line 77 of file SlideGLArea.cs.

77{ get; set; } = -1;

◆ ImageScreenX

float BioGTK.SlideGLArea.ImageScreenX = -1
getset

Definition at line 75 of file SlideGLArea.cs.

75{ get; set; } = -1;

◆ ImageScreenY

float BioGTK.SlideGLArea.ImageScreenY = -1
getset

Definition at line 76 of file SlideGLArea.cs.

76{ get; set; } = -1;

◆ NeedsRedraw

bool BioGTK.SlideGLArea.NeedsRedraw = true
getset

Definition at line 68 of file SlideGLArea.cs.

68{ get; set; } = true;

◆ PreservePreviousFrameWhileIncomplete

bool BioGTK.SlideGLArea.PreservePreviousFrameWhileIncomplete = false
getset

Definition at line 69 of file SlideGLArea.cs.

69{ get; set; } = false;

◆ TilesToRender

List<TileRenderInfo> BioGTK.SlideGLArea.TilesToRender = new()
get

Definition at line 67 of file SlideGLArea.cs.

67{ get; } = new();

Event Documentation

◆ OnSkiaRender

Action<SKCanvas, int, int> BioGTK.SlideGLArea.OnSkiaRender

Event fired during Skia rendering phase for annotation drawing.

Definition at line 83 of file SlideGLArea.cs.


The documentation for this class was generated from the following file: