Skip to content

C#

C# 1

    using Sick.GenIStream;
    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;

    public sealed class DisplayBitmapOptions
    {
        public ushort? Min { get; set; } = null;
        public ushort? Max { get; set; } = null;
        public bool ExcludeZero { get; set; } = true;
        public double Gamma { get; set; } = 1.0;
        public bool Invert { get; set; } = false;
    }

    public static class RulerDisplayBitmap
    {
        /// <summary>
        /// Generates an 8bpp grayscale Bitmap from IFrame for PictureBox display.
        /// 16bpp is normalized to 8bpp using min/max, 8bpp is copied directly.
        /// * This method is responsible for calling frame.Release().
        /// </summary>
        public static Bitmap CreateDisplayBitmap8bpp_Safe(IFrame frame, DisplayBitmapOptions opt = null)
        {
            if (frame == null) throw new ArgumentNullException(nameof(frame));
            if (opt == null) opt = new DisplayBitmapOptions();

            var range = frame.GetRange();
            int w = (int)range.GetWidth();
            int h = (int)range.GetDeliveredHeight();
            int bpp = (int)range.GetBitsPerPixel();    // Expected to be 8 or 16
            int stride = (int)range.GetStride();
            IntPtr src = range.GetData();

            Bitmap bmp = null;
            try
            {
                if (bpp == 8)
                {
                    bmp = CreateFrom8bpp(src, w, h, stride);
                }
                else if (bpp == 16)
                {
                    bmp = CreateFrom16bpp(src, w, h, stride, opt);
                }
                else
                {
                    throw new NotSupportedException("Unsupported BitsPerPixel: " + bpp + ". Only 8 or 16 are supported.");
                }
            }
            finally
            {
                // ★ Must be released once here (not called internally)
                frame.Release();
            }

            return bmp;
        }

        // ===== 8bpp: Copy as is =====
        private static Bitmap CreateFrom8bpp(IntPtr src, int w, int h, int strideSrc)
        {
            var bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

            // Grayscale palette
            var pal = bmp.Palette;
            for (int i = 0; i < 256; i++) pal.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
            bmp.Palette = pal;

            var rect = new Rectangle(0, 0, w, h);
            var data = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
            try
            {
                int strideDst = data.Stride;
                var line = new byte[w];

                for (int y = 0; y < h; y++)
                {
                    Marshal.Copy(src + y * strideSrc, line, 0, w);
                    Marshal.Copy(line, 0, data.Scan0 + y * strideDst, w);
                }
            }
            finally
            {
                bmp.UnlockBits(data);
            }
            return bmp;
        }

        // ===== 16bpp: Min/Max Normalization → 8bpp =====
        private static Bitmap CreateFrom16bpp(IntPtr src, int w, int h, int strideSrc, DisplayBitmapOptions opt)
        {
            ushort min, max;
            if (opt.Min.HasValue && opt.Max.HasValue)
            {
                min = opt.Min.Value;
                max = opt.Max.Value;
            }
            else
            {
                ComputeMinMax16(src, w, h, strideSrc, opt.ExcludeZero, out min, out max);
            }
            if (max <= min) max = (ushort)(min + 1);

            var bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            var pal = bmp.Palette;
            for (int i = 0; i < 256; i++) pal.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
            bmp.Palette = pal;

            var rect = new Rectangle(0, 0, w, h);
            var data = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
            try
            {
                int strideDst = data.Stride;

                // Gamma LUT (0..255 → 0..255)
                double gamma = opt.Gamma < 0.01 ? 0.01 : opt.Gamma;
                byte[] gammaLut = BuildGammaLut(gamma, opt.Invert);

                var srcLine = new byte[w * 2];
                var dstLine = new byte[w];
                double scale = 255.0 / (max - min);

                for (int y = 0; y < h; y++)
                {
                    Marshal.Copy(src + y * strideSrc, srcLine, 0, srcLine.Length);

                    for (int x = 0; x < w; x++)
                    {
                        // little-endian 2 bytes → ushort
                        ushort v = (ushort)(srcLine[(x << 1)] | (srcLine[(x << 1) + 1] << 8));

                        int g = (int)((v - min) * scale + 0.5);
                        if (g < 0) g = 0; else if (g > 255) g = 255;

                        dstLine[x] = gammaLut[g];
                    }

                    Marshal.Copy(dstLine, 0, data.Scan0 + y * strideDst, w);
                }
            }
            finally
            {
                bmp.UnlockBits(data);
            }
            return bmp;
        }

        private static void ComputeMinMax16(IntPtr src, int w, int h, int stride, bool excludeZero, out ushort min, out ushort max)
        {
            min = ushort.MaxValue;
            max = 0;

            var line = new byte[w * 2];
            bool sawAny = false;

            for (int y = 0; y < h; y++)
            {
                Marshal.Copy(src + y * stride, line, 0, line.Length);

                for (int x = 0; x < w; x++)
                {
                    ushort v = (ushort)(line[(x << 1)] | (line[(x << 1) + 1] << 8));
                    if (excludeZero && v == 0) continue;

                    if (v < min) min = v;
                    if (v > max) max = v;
                    sawAny = true;
                }
            }

            if (!sawAny)
            {
                min = 0; max = 1;
            }
        }

        private static byte[] BuildGammaLut(double gamma, bool invert)
        {
            var lut = new byte[256];
            double inv = 1.0 / gamma;
            for (int i = 0; i < 256; i++)
            {
                double n = i / 255.0;
                double g = Math.Pow(n, inv);
                int v = (int)(g * 255.0 + 0.5);
                if (v < 0) v = 0; else if (v > 255) v = 255;
                if (invert) v = 255 - v;
                lut[i] = (byte)v;
            }
            return lut;
        }
    }