123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- using PaintDotNet.SystemLayer;
- using System;
- using System.Drawing;
- using System.Drawing.Imaging;
- namespace PaintDotNet
- {
- /// <summary>
- /// This is our StitchSurface type. We allocate our own blocks of memory for this,
- /// and provide ways to create a GDI+ Bitmap object that aliases our StitchSurface.
- /// That way we can do everything fast, in memory and have complete control,
- /// and still have the ability to use GDI+ for drawing and rendering where
- /// appropriate.
- /// </summary>
- //[Serializable]
- public sealed class StitchSurface : IDisposable
- {
- /// <summary>
- /// 持有的mat
- /// </summary>
- private OpenCvSharp.Mat mat;
- /// <summary>
- /// 持有mat的绘制区域
- /// </summary>
- public Rectangle stitchBounds;
- /// <summary>
- /// 持有mat的绘制区域外背景颜色
- /// </summary>
- public Color stitchBackColor;
- /// <summary>
- /// 图像内存块
- /// </summary>
- public MemoryBlock scan0;
- /// <summary>
- /// 宽
- /// </summary>
- private int width;
- /// <summary>
- /// 高
- /// </summary>
- private int height;
- /// <summary>
- /// 扫描宽度
- /// </summary>
- private int stride;
- /// <summary>
- /// 释放标记
- /// </summary>
- private bool disposed = false;
- /// <summary>
- /// 存储打开时图片的格式
- /// </summary>
- private PixelFormat pixelFormat = PixelFormat.Format24bppRgb;
- public PixelFormat PixelFormat
- {
- get
- {
- return this.pixelFormat;
- }
- set
- {
- this.pixelFormat = value;
- }
- }
- public bool IsDisposed
- {
- get
- {
- return this.disposed;
- }
- }
- /// <summary>
- /// 持有mat的有效区域
- /// </summary>
- public OpenCvSharp.Mat StitchMat
- {
- get
- {
- if (this.mat == null || this.stitchBounds.Width == 0 || this.stitchBounds.Height == 0) return null;
- unsafe
- {
- return new OpenCvSharp.Mat(stitchBounds.Height, stitchBounds.Width, OpenCvSharp.MatType.CV_8UC3, new IntPtr((void*)((byte*)scan0.VoidStar + GetPointByteOffsetUnchecked(stitchBounds.X, stitchBounds.Y, this.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3))), stride);
- }
- }
- }
- /// <summary>
- /// Gets the width, in pixels, of this StitchSurface.
- /// </summary>
- /// <remarks>
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int StitchWidth
- {
- get
- {
- return Math.Min(this.width, this.stitchBounds.Width + 2 * this.stitchBounds.X);
- }
- }
- /// <summary>
- /// Gets the height, in pixels, of this StitchSurface.
- /// </summary>
- /// <remarks>
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int StitchHeight
- {
- get
- {
- return Math.Min(this.height, this.stitchBounds.Height + 2 * this.stitchBounds.Y);
- }
- }
- /// <summary>
- /// Gets the width, in pixels, of this StitchSurface.
- /// </summary>
- /// <remarks>
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int Width
- {
- get
- {
- return this.width;
- }
- }
- /// <summary>
- /// Gets the height, in pixels, of this StitchSurface.
- /// </summary>
- /// <remarks>
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int Height
- {
- get
- {
- return this.height;
- }
- }
- /// <summary>
- /// Gets the stride, in bytes, for this StitchSurface.
- /// </summary>
- /// <remarks>
- /// Stride is defined as the number of bytes between the beginning of a row and
- /// the beginning of the next row. Thus, in loose C notation: stride = (byte *)&this[0, 1] - (byte *)&this[0, 0].
- /// Stride will always be equal to <b>or greater than</b> Width * ColorBgrB.SizeOf.
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int Stride
- {
- set
- {
- this.stride = value;
- }
- }
- /// <summary>
- /// Gets the size, in pixels, of this StitchSurface.
- /// </summary>
- /// <remarks>
- /// This is a convenience function that creates a new Size instance based
- /// on the values of the Width and Height properties.
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public Size Size
- {
- get
- {
- return new Size(this.width, this.height);
- }
- }
- /// <summary>
- /// Gets the bounds of this StitchSurface, in pixels.
- /// </summary>
- /// <remarks>
- /// This is a convenience function that returns Rectangle(0, 0, Width, Height).
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public Rectangle Bounds
- {
- get
- {
- return new Rectangle(0, 0, width, height);
- }
- }
- /// <summary>
- /// Creates a new instance of the StitchSurface class.
- /// </summary>
- /// <param name="size">The size, in pixels, of the new StitchSurface.</param>
- public StitchSurface(Size size, int SizeOf = 4, OpenCvSharp.Mat mat = null)
- {
- this.mat = mat;
- int width = size.Width;
- int height = size.Height;
- int stride = 0;
- long bytes;
- try
- {
- stride = checked(width * SizeOf);
- bytes = (long)height * (long)stride;
- }
- catch (OverflowException ex)
- {
- throw new OutOfMemoryException("Dimensions are too large - not enough memory, width=" + width.ToString() + ", height=" + height.ToString(), ex);
- }
- MemoryBlock scan0 = new MemoryBlock(width, height, SizeOf);
- this.width = width;
- this.height = height;
- this.stride = stride;
- this.scan0 = scan0;
- }
- /// <summary>
- /// Creates a new instance of the StitchSurface class that reuses a block of memory that was previously allocated.
- /// </summary>
- /// <param name="width">The width, in pixels, for the StitchSurface.</param>
- /// <param name="height">The height, in pixels, for the StitchSurface.</param>
- /// <param name="stride">The stride, in bytes, for the StitchSurface.</param>
- /// <param name="scan0">The MemoryBlock to use. The beginning of this buffer defines the upper left (0, 0) pixel of the StitchSurface.</param>
- private StitchSurface(int width, int height, int stride, MemoryBlock scan0)
- {
- this.width = width;
- this.height = height;
- this.stride = stride;
- this.scan0 = scan0;
- }
- ~StitchSurface()
- {
- Dispose(false);
- }
- /// <summary>
- /// Creates a StitchSurface that aliases a portion of this StitchSurface.
- /// </summary>
- /// <param name="bounds">The portion of this StitchSurface that will be aliased.</param>
- /// <remarks>The upper left corner of the new StitchSurface will correspond to the
- /// upper left corner of this rectangle in the original StitchSurface.</remarks>
- /// <returns>A StitchSurface that aliases the requested portion of this StitchSurface.</returns>
- public StitchSurface CreateWindow(Rectangle bounds)
- {
- return CreateWindow(bounds.X, bounds.Y, bounds.Width, bounds.Height);
- }
- public StitchSurface CreateWindow(int x, int y, int windowWidth, int windowHeight)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("StitchSurface");
- }
- if (windowHeight == 0)
- {
- throw new ArgumentOutOfRangeException("windowHeight", "must be greater than zero");
- }
- Rectangle original = this.Bounds;
- Rectangle sub = new Rectangle(x, y, windowWidth, windowHeight);
- Rectangle clipped = Rectangle.Intersect(original, sub);
- if (clipped != sub)
- {
- throw new ArgumentOutOfRangeException("bounds", new Rectangle(x, y, windowWidth, windowHeight),
- "bounds parameters must be a subset of this StitchSurface's bounds");
- }
- long offset = ((long)stride * (long)y) + ((long)(this.pixelFormat == PixelFormat.Format24bppRgb ? 3 : 4) * (long)x);
- long length = ((windowHeight - 1) * (long)stride) + (long)windowWidth * (long)(this.pixelFormat == PixelFormat.Format24bppRgb ? 3 : 4);
- MemoryBlock block = new MemoryBlock(this.scan0, offset, length);
- return new StitchSurface(windowWidth, windowHeight, this.stride, block);
- }
- /// <summary>
- /// Gets the offset, in bytes, of the requested row from the start of the StitchSurface.
- /// </summary>
- /// <param name="y">The row.</param>
- /// <returns>The number of bytes between (0,0) and (0,y)</returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetRowByteOffset().
- /// </remarks>
- public unsafe long GetRowByteOffsetUnchecked(int y)
- {
- #if DEBUG
- if (y < 0 || y >= this.height)
- {
- Tracing.Ping("y=" + y.ToString() + " is out of bounds of [0, " + this.height.ToString() + ")");
- }
- #endif
- return (long)y * (long)stride;
- }
- /// <summary>
- /// Gets a pointer to the beginning of the requested row in the StitchSurface.
- /// </summary>
- /// <param name="y">The row</param>
- /// <returns>A pointer that references (0,y) in this StitchSurface.</returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetRowAddress().
- /// </remarks>
- public unsafe ColorBgrB* GetRowAddressUnchecked(int y)
- {
- #if DEBUG
- if (y < 0 || y >= this.height)
- {
- Tracing.Ping("y=" + y.ToString() + " is out of bounds of [0, " + this.height.ToString() + ")");
- }
- #endif
- return (ColorBgrB*)(((byte*)scan0.VoidStar) + GetRowByteOffsetUnchecked(y));
- }
- /// <summary>
- /// Gets the number of bytes from the beginning of a row to the requested column.
- /// </summary>
- /// <param name="x">The column.</param>
- /// <returns>
- /// The number of bytes between (0,n) and (x,n) where n is in the range [0, Height).
- /// </returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetColumnByteOffset().
- /// </remarks>
- public long GetColumnByteOffsetUnchecked(int x, int SizeOf)
- {
- #if DEBUG
- if (x < 0 || x >= this.width)
- {
- Tracing.Ping("x=" + x.ToString() + " is out of bounds of [0, " + this.width.ToString() + ")");
- }
- #endif
- return (long)x * (long)SizeOf;
- }
- /// <summary>
- /// Gets the number of bytes from the beginning of the StitchSurface's buffer to
- /// the requested point.
- /// </summary>
- /// <param name="x">The x offset.</param>
- /// <param name="y">The y offset.</param>
- /// <returns>
- /// The number of bytes between (0,0) and (x,y).
- /// </returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetPointByteOffset().
- /// </remarks>
- public long GetPointByteOffsetUnchecked(int x, int y, int SizeOf)
- {
- #if DEBUG
- if (x < 0 || x >= this.width)
- {
- Tracing.Ping("x=" + x.ToString() + " is out of bounds of [0, " + this.width.ToString() + ")");
- }
- if (y < 0 || y >= this.height)
- {
- Tracing.Ping("y=" + y.ToString() + " is out of bounds of [0, " + this.height.ToString() + ")");
- }
- #endif
- return GetRowByteOffsetUnchecked(y) + GetColumnByteOffsetUnchecked(x, SizeOf);
- }
- /// <summary>
- /// Gets the address in memory of the requested point.
- /// </summary>
- /// <param name="x">The x offset.</param>
- /// <param name="y">The y offset.</param>
- /// <returns>A pointer to the requested point in the StitchSurface.</returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetPointAddress().
- /// </remarks>
- public unsafe ColorBgrB* GetPointAddressUnchecked(int x, int y)
- {
- #if DEBUG
- if (x < 0 || x >= this.width)
- {
- Tracing.Ping("x=" + x.ToString() + " is out of bounds of [0, " + this.width.ToString() + ")");
- }
- if (y < 0 || y >= this.height)
- {
- Tracing.Ping("y=" + y.ToString() + " is out of bounds of [0, " + this.height.ToString() + ")");
- }
- #endif
- return unchecked(x + (ColorBgrB*)(((byte*)scan0.VoidStar) + (y * stride)));
- }
- /// <summary>
- /// Determines if the requested row offset is within bounds.
- /// </summary>
- /// <param name="y">The row.</param>
- /// <returns>true if y >= 0 and y < height, otherwise false</returns>
- public bool IsRowVisible(int y)
- {
- return y >= 0 && y < Height;
- }
- /// <summary>
- /// Determines if the requested column offset is within bounds.
- /// </summary>
- /// <param name="x">The column.</param>
- /// <returns>true if x >= 0 and x < width, otherwise false.</returns>
- public bool IsColumnVisible(int x)
- {
- return x >= 0 && x < Width;
- }
- /// <summary>
- /// Helper function. Same as calling CreateAliasedBounds(Bounds).
- /// </summary>
- /// <returns>A GDI+ Bitmap that aliases the entire StitchSurface.</returns>
- public Bitmap CreateAliasedBitmap()
- {
- return CreateAliasedBitmap(this.Bounds);
- }
- /// <summary>
- /// Helper function. Same as calling CreateAliasedBounds(bounds, true).
- /// </summary>
- /// <returns>A GDI+ Bitmap that aliases the entire StitchSurface.</returns>
- public Bitmap CreateAliasedBitmap(Rectangle bounds)
- {
- return CreateAliasedBitmap(bounds, true);
- }
- /// <summary>
- /// Creates a GDI+ Bitmap object that aliases the same memory that this StitchSurface does.
- /// Then you can use GDI+ to draw on to this StitchSurface.
- /// Note: Since the Bitmap does not hold a reference to this StitchSurface object, nor to
- /// the MemoryBlock that it contains, you must hold a reference to the StitchSurface object
- /// for as long as you wish to use the aliased Bitmap. Otherwise the memory may be
- /// freed and the Bitmap will look corrupt or cause other errors. You may use the
- /// RenderArgs class to help manage this lifetime instead.
- /// </summary>
- /// <param name="bounds">The rectangle of interest within this StitchSurface that you wish to alias.</param>
- /// <param name="alpha">If true, the returned bitmap will use PixelFormat.Format32bppArgb.
- /// If false, the returned bitmap will use PixelFormat.Format32bppRgb.</param>
- /// <returns>A GDI+ Bitmap that aliases the requested portion of the StitchSurface.</returns>
- /// <exception cref="ArgumentOutOfRangeException"><b>bounds</b> was not entirely within the boundaries of the StitchSurface</exception>
- /// <exception cref="ObjectDisposedException">This StitchSurface instance is already disposed.</exception>
- public Bitmap CreateAliasedBitmap(Rectangle bounds, bool alpha = true)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("StitchSurface");
- }
- if (bounds.IsEmpty)
- {
- throw new ArgumentOutOfRangeException();
- }
- Rectangle clipped = Rectangle.Intersect(this.Bounds, bounds);
- if (clipped != bounds)
- {
- throw new ArgumentOutOfRangeException();
- }
- unsafe
- {
- OpenCvSharp.Mat temp = new OpenCvSharp.Mat(bounds.Height, bounds.Width,OpenCvSharp.MatType.CV_8UC3, new IntPtr((void*)((byte*)scan0.VoidStar + GetPointByteOffsetUnchecked(bounds.X, bounds.Y, this.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3))), stride);
- Bitmap bitmap1;
- if (temp.Rows < 3000)
- bitmap1 = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(temp);
- else
- bitmap1 = new Bitmap(temp.Width, temp.Height, (int)temp.Step(), PixelFormat.Format24bppRgb, temp.Data);
- return bitmap1;// new Bitmap(temp.Width, temp.Height, (int)temp.Step(), PixelFormat.Format24bppRgb, temp.Data);
- //return new Bitmap(bounds.Width, bounds.Height, (stride / 4) * 4, /*alpha ? PixelFormat.Format32bppArgb :*/ this.PixelFormat/*PixelFormat.Format24bppRgb*/,
- // new IntPtr((void*)((byte*)scan0.VoidStar + GetPointByteOffsetUnchecked(bounds.X, bounds.Y, this.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3))));
- }
- }
- /// <summary>
- /// Releases all resources held by this StitchSurface object.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- private void Dispose(bool disposing)
- {
- if (!disposed)
- {
- disposed = true;
- if (disposing)
- {
- scan0.Dispose();
- scan0 = null;
- if (mat != null)
- {
- mat.Dispose();
- mat = null;
- }
- }
- }
- }
- }
- }
|