1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507 |
- using PaintDotNet.SystemLayer;
- using System;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Imaging;
- namespace PaintDotNet
- {
- /// <summary>
- /// This is our Surface type. We allocate our own blocks of memory for this,
- /// and provide ways to create a GDI+ Bitmap object that aliases our surface.
- /// 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 Surface : IDisposable, ICloneable
- {
- /// <summary>
- /// 持有的mat
- /// </summary>
- public OpenCvSharp.Mat mat;
- /// <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.Format32bppArgb;
- /// <summary>
- /// 缩略图
- /// </summary>
- private Bitmap thumbnail;
- /// <summary>
- /// 备份的mat
- /// </summary>
- private OpenCvSharp.Mat backUpMat;
- public OpenCvSharp.Mat BackUpMat
- {
- get
- {
- if (this.backUpMat == null)
- this.backUpMat = this.mat.Clone();
- return this.backUpMat.Clone();
- }
- set
- {
- this.backUpMat = value.Clone();
- }
- }
- public Bitmap Thumbnail
- {
- get
- {
- if (this.thumbnail == null)
- CreateThumbnail();
- return this.thumbnail;
- }
- }
- /// <summary>
- /// 创建缩略图
- /// </summary>
- public void CreateThumbnail()
- {
- //TODO 回头去掉
- if (this.mat == null)
- this.mat = PaintDotNet.Camera.Tools.ToMat(CreateAliasedBitmap());
- //Bitmap origin = CreateAliasedBitmap();
- Bitmap bitmap = null;
- if (this.mat.Width > this.mat.Height)
- {
- bitmap = MakeThumbnail(this.mat, 90, 90, "W");
- }
- else if (this.mat.Height > this.mat.Width)
- {
- bitmap = MakeThumbnail(this.mat, 90, 90, "H");
- }
- else
- {
- bitmap = MakeThumbnail(this.mat, 90, 90, "W");
- }
- this.thumbnail = bitmap;
- }
- public PixelFormat PixelFormat
- {
- get
- {
- return this.pixelFormat;
- }
- set
- {
- this.pixelFormat = value;
- }
- }
- public bool IsDisposed
- {
- get
- {
- return this.disposed;
- }
- }
- /// <summary>
- /// Gets a MemoryBlock which is the buffer holding the pixels associated
- /// with this Surface.
- /// </summary>
- public MemoryBlock Scan0
- {
- get
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- return this.scan0;
- }
- }
- /// <summary>
- /// Gets the width, in pixels, of this Surface.
- /// </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 Surface.
- /// </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 Surface.
- /// </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 * ColorBgra.SizeOf.
- /// This property will never throw an ObjectDisposedException.
- /// </remarks>
- public int Stride
- {
- get
- {
- return this.stride;
- }
- set
- {
- this.stride = value;
- }
- }
- /// <summary>
- /// Gets the size, in pixels, of this Surface.
- /// </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 Surface, 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 Surface class.
- /// </summary>
- /// <param name="size">The size, in pixels, of the new Surface.</param>
- public Surface(Size size)
- : this(size.Width, size.Height)
- {
- }
- public Surface(Size size, PixelFormat pixelFormat)
- : this(size.Width, size.Height, pixelFormat)
- {
- }
- /// <summary>
- /// Creates a new instance of the Surface class.
- /// </summary>
- /// <param name="width">The width, in pixels, of the new Surface.</param>
- /// <param name="height">The height, in pixels, of the new Surface.</param>
- public Surface(int width, int height)
- {
- int stride = 0;
- long bytes;
- try
- {
- stride = checked(width * ColorBgra.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);
- Create(width, height, stride, scan0);
- }
- public Surface(int width, int height, PixelFormat pixelFormat)
- {
- int ch;
- PixelFormat = pixelFormat;
- try
- {
- if (pixelFormat == PixelFormat.Format24bppRgb)
- ch = 3;
- else if (pixelFormat == PixelFormat.Format8bppIndexed)
- ch = 1;
- else
- ch = 4;
- }
- 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, ch);
- Create(width, height, width * ch, scan0);
- }
- /// <summary>
- /// Creates a new instance of the Surface class that reuses a block of memory that was previously allocated.
- /// </summary>
- /// <param name="width">The width, in pixels, for the Surface.</param>
- /// <param name="height">The height, in pixels, for the Surface.</param>
- /// <param name="stride">The stride, in bytes, for the Surface.</param>
- /// <param name="scan0">The MemoryBlock to use. The beginning of this buffer defines the upper left (0, 0) pixel of the Surface.</param>
- private Surface(int width, int height, int stride, MemoryBlock scan0)
- {
- Create(width, height, stride, scan0);
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "width")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "height")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "stride")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "scan0")]
- private void Create(int width, int height, int stride, MemoryBlock scan0)
- {
- this.width = width;
- this.height = height;
- this.stride = stride;
- this.scan0 = scan0;
- }
- ~Surface()
- {
- Dispose(false);
- }
- /// <summary>
- /// Creates a Surface that aliases a portion of this Surface.
- /// </summary>
- /// <param name="bounds">The portion of this Surface that will be aliased.</param>
- /// <remarks>The upper left corner of the new Surface will correspond to the
- /// upper left corner of this rectangle in the original Surface.</remarks>
- /// <returns>A Surface that aliases the requested portion of this Surface.</returns>
- public Surface CreateWindow(Rectangle bounds)
- {
- return CreateWindow(bounds.X, bounds.Y, bounds.Width, bounds.Height);
- }
- public Surface CreateWindow(int x, int y, int windowWidth, int windowHeight)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- 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 Surface's bounds");
- }
- long offset = ((long)stride * (long)y) + ((long)ColorBgra.SizeOf * (long)x);
- long length = ((windowHeight - 1) * (long)stride) + (long)windowWidth * (long)ColorBgra.SizeOf;
- MemoryBlock block = new MemoryBlock(this.scan0, offset, length);
- return new Surface(windowWidth, windowHeight, this.stride, block);
- }
- /// <summary>
- /// Gets the offset, in bytes, of the requested row from the start of the surface.
- /// </summary>
- /// <param name="y">The row.</param>
- /// <returns>The number of bytes between (0,0) and (0,y).</returns>
- public long GetRowByteOffset(int y)
- {
- if (y < 0 || y >= height)
- {
- throw new ArgumentOutOfRangeException("y", "Out of bounds: y=" + y.ToString());
- }
- return (long)y * (long)stride;
- }
- /// <summary>
- /// Gets the offset, in bytes, of the requested row from the start of the surface.
- /// </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)
- {
- return (long)y * (long)stride;
- }
- /// <summary>
- /// Gets a pointer to the beginning of the requested row in the surface.
- /// </summary>
- /// <param name="y">The row</param>
- /// <returns>A pointer that references (0,y) in this surface.</returns>
- /// <remarks>Since this returns a pointer, it is potentially unsafe to use.</remarks>
- public unsafe ColorBgra* GetRowAddress(int y)
- {
- return (ColorBgra*)(((byte*)scan0.VoidStar) + GetRowByteOffset(y));
- }
- /// <summary>
- /// Gets a pointer to the beginning of the requested row in the surface.
- /// </summary>
- /// <param name="y">The row</param>
- /// <returns>A pointer that references (0,y) in this surface.</returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetRowAddress().
- /// </remarks>
- public unsafe ColorBgra* GetRowAddressUnchecked(int y)
- {
- return (ColorBgra*)(((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)
- {
- return (long)x * (long)ColorBgra.SizeOf;
- }
- /// <summary>
- /// Gets the number of bytes from the beginning of the surface'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)
- {
- return GetRowByteOffsetUnchecked(y) + GetColumnByteOffsetUnchecked(x);
- }
- /// <summary>
- /// Gets the color at a specified point in the surface.
- /// </summary>
- /// <param name="x">The x offset.</param>
- /// <param name="y">The y offset.</param>
- /// <returns>The color at the requested location.</returns>
- public ColorBgra GetPoint(int x, int y)
- {
- return this[x, y];
- }
- /// <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 surface.</returns>
- /// <remarks>Since this method returns a pointer, it is potentially unsafe to use.</remarks>
- public unsafe ColorBgra* GetPointAddress(int x, int y)
- {
- if (x < 0 || x >= Width)
- {
- throw new ArgumentOutOfRangeException("x", "Out of bounds: x=" + x.ToString());
- }
- return GetRowAddress(y) + x;
- }
- /// <summary>
- /// Gets the address in memory of the requested point.
- /// </summary>
- /// <param name="pt">The point to retrieve.</param>
- /// <returns>A pointer to the requested point in the surface.</returns>
- /// <remarks>Since this method returns a pointer, it is potentially unsafe to use.</remarks>
- public unsafe ColorBgra* GetPointAddress(Point pt)
- {
- return GetPointAddress(pt.X, pt.Y);
- }
- /// <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 surface.</returns>
- /// <remarks>
- /// This method does not do any bounds checking and is potentially unsafe to use,
- /// but faster than GetPointAddress().
- /// </remarks>
- public unsafe ColorBgra* GetPointAddressUnchecked(int x, int y)
- {
- return unchecked(x + (ColorBgra*)(((byte*)scan0.VoidStar) + (y * stride)));
- }
- /// <summary>
- /// Gets a MemoryBlock that references the row requested.
- /// </summary>
- /// <param name="y">The row.</param>
- /// <returns>A MemoryBlock that gives access to the bytes in the specified row.</returns>
- /// <remarks>This method is the safest to use for direct memory access to a row's pixel data.</remarks>
- public MemoryBlock GetRow(int y)
- {
- return new MemoryBlock(scan0, GetRowByteOffset(y), (long)width * (long)ColorBgra.SizeOf);
- }
- /// <summary>
- /// Determines if the requested pixel coordinate is within bounds.
- /// </summary>
- /// <param name="x">The x coordinate.</param>
- /// <param name="y">The y coordinate.</param>
- /// <returns>true if (x,y) is in bounds, false if it's not.</returns>
- public bool IsVisible(int x, int y)
- {
- return x >= 0 && x < width && y >= 0 && y < height;
- }
- /// <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>
- /// Gets or sets the pixel value at the requested offset.
- /// </summary>
- /// <remarks>
- /// This property is implemented with correctness and error checking in mind. If performance
- /// is a concern, do not use it.
- /// </remarks>
- public ColorBgra this[int x, int y]
- {
- get
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- if (x < 0 || y < 0 || x >= this.width || y >= this.height)
- {
- throw new ArgumentOutOfRangeException("(x,y)", new Point(x, y), "Coordinates out of range, max=" + new Size(width - 1, height - 1).ToString());
- }
- unsafe
- {
- return *GetPointAddressUnchecked(x, y);
- }
- }
- set
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- if (x < 0 || y < 0 || x >= this.width || y >= this.height)
- {
- throw new ArgumentOutOfRangeException("(x,y)", new Point(x, y), "Coordinates out of range, max=" + new Size(width - 1, height - 1).ToString());
- }
- unsafe
- {
- *GetPointAddressUnchecked(x, y) = value;
- }
- }
- }
- /// <summary>
- /// Helper function. Same as calling CreateAliasedBounds(Bounds).
- /// </summary>
- /// <returns>A GDI+ Bitmap that aliases the entire Surface.</returns>
- public Bitmap CreateAliasedBitmap(bool alpha)
- {
- return CreateAliasedBitmap(this.Bounds, alpha);
- }
- public OpenCvSharp.Mat CreatedAliasedMat()
- {
- if (mat == null)//<-CreateAliasedBitmap
- return Camera.Tools.ToMat(this.CreateAliasedBitmap());// OpenCvSharp.Extensions.BitmapConverter.ToMat(this.CreateAliasedBitmap());
- return mat/*.Clone()*/;//待测试
- }
- /// <summary>
- /// Helper function. Same as calling CreateAliasedBounds(Bounds).
- /// </summary>
- /// <returns>A GDI+ Bitmap that aliases the entire Surface.</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 Surface.</returns>
- public Bitmap CreateAliasedBitmap(Rectangle bounds)
- {
- return CreateAliasedBitmap(bounds, true);
- }
- /// <summary>
- /// Creates a GDI+ Bitmap object that aliases the same memory that this Surface does.
- /// Then you can use GDI+ to draw on to this surface.
- /// Note: Since the Bitmap does not hold a reference to this Surface object, nor to
- /// the MemoryBlock that it contains, you must hold a reference to the Surface 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 Surface 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 Surface.</returns>
- /// <exception cref="ArgumentOutOfRangeException"><b>bounds</b> was not entirely within the boundaries of the Surface</exception>
- /// <exception cref="ObjectDisposedException">This Surface instance is already disposed.</exception>
- public Bitmap CreateAliasedBitmap(Rectangle bounds, bool alpha = true)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- if (bounds.IsEmpty)
- {
- throw new ArgumentOutOfRangeException();
- }
- Rectangle clipped = Rectangle.Intersect(this.Bounds, bounds);
- if (clipped != bounds)
- {
- throw new ArgumentOutOfRangeException();
- }
- unsafe
- {
- return new Bitmap(bounds.Width, bounds.Height, stride, alpha ? PixelFormat.Format32bppArgb : this.PixelFormat,
- new IntPtr((void*)((byte*)scan0.VoidStar + GetPointByteOffsetUnchecked(bounds.X, bounds.Y))));
- }
- }
- /// <summary>
- /// Creates a new Surface and copies the pixels from a Bitmap to it.
- /// </summary>
- /// <param name="bitmap">The Bitmap to duplicate.</param>
- /// <returns>A new Surface that is the same size as the given Bitmap and that has the same pixel values.</returns>
- public static Surface CopyFromBitmap(Bitmap bitmap)
- {
- Surface surface = new Surface(bitmap.Width, bitmap.Height);
- BitmapData bd = bitmap.LockBits(surface.Bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
- unsafe
- {
- for (int y = 0; y < bd.Height; ++y)
- {
- Memory.Copy((void*)surface.GetRowAddress(y),
- (byte*)bd.Scan0.ToPointer() + (y * bd.Stride), (ulong)bd.Width * ColorBgra.SizeOf);
- }
- }
- bitmap.UnlockBits(bd);
- return surface;
- }
- /// <summary>
- /// Copies the contents of the given surface to the upper left corner of this surface.
- /// </summary>
- /// <param name="source">The surface to copy pixels from.</param>
- /// <remarks>
- /// The source surface does not need to have the same dimensions as this surface. Clipping
- /// will be handled automatically. No resizing will be done.
- /// </remarks>
- public void CopySurface(Surface source)
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- if (this.stride == source.stride &&
- (this.width * ColorBgra.SizeOf) == this.stride &&
- this.width == source.width &&
- this.height == source.height)
- {
- unsafe
- {
- Memory.Copy(this.scan0.VoidStar,
- source.scan0.VoidStar,
- ((ulong)(height - 1) * (ulong)stride) + ((ulong)width * (ulong)ColorBgra.SizeOf));
- }
- }
- else
- {
- int copyWidth = Math.Min(width, source.width);
- int copyHeight = Math.Min(height, source.height);
- unsafe
- {
- for (int y = 0; y < copyHeight; ++y)
- {
- Memory.Copy(GetRowAddressUnchecked(y), source.GetRowAddressUnchecked(y), (ulong)copyWidth * (ulong)ColorBgra.SizeOf);
- }
- }
- }
- }
- object ICloneable.Clone()
- {
- return Clone();
- }
- /// <summary>
- /// Creates a new surface with the same dimensions and pixel values as this one.
- /// </summary>
- /// <returns>A new surface that is a clone of the current one.</returns>
- public Surface Clone()
- {
- if (disposed)
- {
- throw new ObjectDisposedException("Surface");
- }
- Surface ret = new Surface(this.Size);
- ret.CopySurface(this);
- return ret;
- }
- /// <summary>
- /// Clears the surface to all-white (BGRA = [255,255,255,255]).
- /// </summary>
- public void Clear()
- {
- Clear(ColorBgra.FromBgra(255, 255, 255, 255));
- }
- /// <summary>
- /// Clears the surface to the given color value.
- /// </summary>
- /// <param name="color">The color value to fill the surface with.</param>
- public void Clear(ColorBgra color)
- {
- //new UnaryPixelOps.Constant(color).Apply(this, this.Bounds);
- }
- /// <summary>
- /// Fits the source surface to this surface using super sampling. If the source surface is less wide
- /// or less tall than this surface (i.e. magnification), bicubic resampling is used instead. If either
- /// the source or destination has a dimension that is only 1 pixel, nearest neighbor is used.
- /// </summary>
- /// <param name="source">The surface to read pixels from.</param>
- /// <param name="dstRoi">The rectangle to clip rendering to.</param>
- /// <remarks>This method was implemented with correctness, not performance, in mind.</remarks>
- public void SuperSamplingFitSurface(Surface source, Rectangle dstRoi)
- {
- if (source.Width == Width && source.Height == Height)
- {
- CopySurface(source);
- }
- else if (source.Width <= Width || source.Height <= Height)
- {
- if (source.width < 2 || source.height < 2 || this.width < 2 || this.height < 2)
- {
- this.NearestNeighborFitSurface(source, dstRoi);
- }
- else
- {
- this.BicubicFitSurface(source, dstRoi);
- }
- }
- else unsafe
- {
- Rectangle dstRoi2 = Rectangle.Intersect(dstRoi, this.Bounds);
- for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY)
- {
- double srcTop = (double)(dstY * source.height) / (double)height;
- double srcTopFloor = Math.Floor(srcTop);
- double srcTopWeight = 1 - (srcTop - srcTopFloor);
- int srcTopInt = (int)srcTopFloor;
- double srcBottom = (double)((dstY + 1) * source.height) / (double)height;
- double srcBottomFloor = Math.Floor(srcBottom - 0.00001);
- double srcBottomWeight = srcBottom - srcBottomFloor;
- int srcBottomInt = (int)srcBottomFloor;
- ColorBgra* dstPtr = this.GetPointAddressUnchecked(dstRoi2.Left, dstY);
- for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX)
- {
- double srcLeft = (double)(dstX * source.width) / (double)width;
- double srcLeftFloor = Math.Floor(srcLeft);
- double srcLeftWeight = 1 - (srcLeft - srcLeftFloor);
- int srcLeftInt = (int)srcLeftFloor;
- double srcRight = (double)((dstX + 1) * source.width) / (double)width;
- double srcRightFloor = Math.Floor(srcRight - 0.00001);
- double srcRightWeight = srcRight - srcRightFloor;
- int srcRightInt = (int)srcRightFloor;
- double blueSum = 0;
- double greenSum = 0;
- double redSum = 0;
- double alphaSum = 0;
- // left fractional edge
- ColorBgra* srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1);
- for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
- {
- double a = srcLeftPtr->A;
- blueSum += srcLeftPtr->B * srcLeftWeight * a;
- greenSum += srcLeftPtr->G * srcLeftWeight * a;
- redSum += srcLeftPtr->R * srcLeftWeight * a;
- alphaSum += srcLeftPtr->A * srcLeftWeight;
- srcLeftPtr = (ColorBgra*)((byte*)srcLeftPtr + source.stride);
- }
- // right fractional edge
- ColorBgra* srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1);
- for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
- {
- double a = srcRightPtr->A;
- blueSum += srcRightPtr->B * srcRightWeight * a;
- greenSum += srcRightPtr->G * srcRightWeight * a;
- redSum += srcRightPtr->R * srcRightWeight * a;
- alphaSum += srcRightPtr->A * srcRightWeight;
- srcRightPtr = (ColorBgra*)((byte*)srcRightPtr + source.stride);
- }
- // top fractional edge
- ColorBgra* srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt);
- for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
- {
- double a = srcTopPtr->A;
- blueSum += srcTopPtr->B * srcTopWeight * a;
- greenSum += srcTopPtr->G * srcTopWeight * a;
- redSum += srcTopPtr->R * srcTopWeight * a;
- alphaSum += srcTopPtr->A * srcTopWeight;
- ++srcTopPtr;
- }
- // bottom fractional edge
- ColorBgra* srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt);
- for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
- {
- double a = srcBottomPtr->A;
- blueSum += srcBottomPtr->B * srcBottomWeight * a;
- greenSum += srcBottomPtr->G * srcBottomWeight * a;
- redSum += srcBottomPtr->R * srcBottomWeight * a;
- alphaSum += srcBottomPtr->A * srcBottomWeight;
- ++srcBottomPtr;
- }
- // center area
- for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
- {
- ColorBgra* srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY);
- for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
- {
- double a = srcPtr->A;
- blueSum += (double)srcPtr->B * a;
- greenSum += (double)srcPtr->G * a;
- redSum += (double)srcPtr->R * a;
- alphaSum += (double)srcPtr->A;
- ++srcPtr;
- }
- }
- // four corner pixels
- ColorBgra srcTL = source.GetPoint(srcLeftInt, srcTopInt);
- double srcTLA = srcTL.A;
- blueSum += srcTL.B * (srcTopWeight * srcLeftWeight) * srcTLA;
- greenSum += srcTL.G * (srcTopWeight * srcLeftWeight) * srcTLA;
- redSum += srcTL.R * (srcTopWeight * srcLeftWeight) * srcTLA;
- alphaSum += srcTL.A * (srcTopWeight * srcLeftWeight);
- ColorBgra srcTR = source.GetPoint(srcRightInt, srcTopInt);
- double srcTRA = srcTR.A;
- blueSum += srcTR.B * (srcTopWeight * srcRightWeight) * srcTRA;
- greenSum += srcTR.G * (srcTopWeight * srcRightWeight) * srcTRA;
- redSum += srcTR.R * (srcTopWeight * srcRightWeight) * srcTRA;
- alphaSum += srcTR.A * (srcTopWeight * srcRightWeight);
- ColorBgra srcBL = source.GetPoint(srcLeftInt, srcBottomInt);
- double srcBLA = srcBL.A;
- blueSum += srcBL.B * (srcBottomWeight * srcLeftWeight) * srcBLA;
- greenSum += srcBL.G * (srcBottomWeight * srcLeftWeight) * srcBLA;
- redSum += srcBL.R * (srcBottomWeight * srcLeftWeight) * srcBLA;
- alphaSum += srcBL.A * (srcBottomWeight * srcLeftWeight);
- ColorBgra srcBR = source.GetPoint(srcRightInt, srcBottomInt);
- double srcBRA = srcBR.A;
- blueSum += srcBR.B * (srcBottomWeight * srcRightWeight) * srcBRA;
- greenSum += srcBR.G * (srcBottomWeight * srcRightWeight) * srcBRA;
- redSum += srcBR.R * (srcBottomWeight * srcRightWeight) * srcBRA;
- alphaSum += srcBR.A * (srcBottomWeight * srcRightWeight);
- double area = (srcRight - srcLeft) * (srcBottom - srcTop);
- double alpha = alphaSum / area;
- double blue;
- double green;
- double red;
- if (alpha == 0)
- {
- blue = 0;
- green = 0;
- red = 0;
- }
- else
- {
- blue = blueSum / alphaSum;
- green = greenSum / alphaSum;
- red = redSum / alphaSum;
- }
- // add 0.5 so that rounding goes in the direction we want it to
- blue += 0.5;
- green += 0.5;
- red += 0.5;
- alpha += 0.5;
- dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
- ++dstPtr;
- }
- }
- }
- }
- /// <summary>
- /// Fits the source surface to this surface using nearest neighbor resampling.
- /// </summary>
- /// <param name="source">The surface to read pixels from.</param>
- /// <param name="dstRoi">The rectangle to clip rendering to.</param>
- public void NearestNeighborFitSurface(Surface source, Rectangle dstRoi)
- {
- Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
- unsafe
- {
- for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
- {
- int srcY = (dstY * source.height) / height;
- ColorBgra* srcRow = source.GetRowAddressUnchecked(srcY);
- ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
- for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
- {
- int srcX = (dstX * source.width) / width;
- *dstPtr = *(srcRow + srcX);
- ++dstPtr;
- }
- }
- }
- }
- private double CubeClamped(double x)
- {
- if (x >= 0)
- {
- return x * x * x;
- }
- else
- {
- return 0;
- }
- }
- /// <summary>
- /// Implements R() as defined at http://astronomy.swin.edu.au/%7Epbourke/colour/bicubic/
- /// </summary>
- private double R(double x)
- {
- return (CubeClamped(x + 2) - (4 * CubeClamped(x + 1)) + (6 * CubeClamped(x)) - (4 * CubeClamped(x - 1))) / 6;
- }
- /// <summary>
- /// Fits the source surface to this surface using bicubic interpolation.
- /// </summary>
- /// <param name="source">The Surface to read pixels from.</param>
- /// <param name="dstRoi">The rectangle to clip rendering to.</param>
- /// <remarks>
- /// This method was implemented with correctness, not performance, in mind.
- /// Based on: "Bicubic Interpolation for Image Scaling" by Paul Bourke,
- /// http://astronomy.swin.edu.au/%7Epbourke/colour/bicubic/
- /// </remarks>
- public void BicubicFitSurface(Surface source, Rectangle dstRoi)
- {
- float leftF = (1 * (float)(width - 1)) / (float)(source.width - 1);
- float topF = (1 * (height - 1)) / (float)(source.height - 1);
- float rightF = ((float)(source.width - 3) * (float)(width - 1)) / (float)(source.width - 1);
- float bottomF = ((float)(source.Height - 3) * (float)(height - 1)) / (float)(source.height - 1);
- int left = (int)Math.Ceiling((double)leftF);
- int top = (int)Math.Ceiling((double)topF);
- int right = (int)Math.Floor((double)rightF);
- int bottom = (int)Math.Floor((double)bottomF);
- Rectangle[] rois = new Rectangle[] {
- Rectangle.FromLTRB(left, top, right, bottom),
- new Rectangle(0, 0, width, top),
- new Rectangle(0, top, left, height - top),
- new Rectangle(right, top, width - right, height - top),
- new Rectangle(left, bottom, right - left, height - bottom)
- };
- for (int i = 0; i < rois.Length; ++i)
- {
- rois[i].Intersect(dstRoi);
- if (rois[i].Width > 0 && rois[i].Height > 0)
- {
- if (i == 0)
- {
- BicubicFitSurfaceUnchecked(source, rois[i]);
- }
- else
- {
- BicubicFitSurfaceChecked(source, rois[i]);
- }
- }
- }
- }
- /// <summary>
- /// Implements bicubic filtering with bounds checking at every pixel.
- /// </summary>
- private void BicubicFitSurfaceChecked(Surface source, Rectangle dstRoi)
- {
- if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2)
- {
- SuperSamplingFitSurface(source, dstRoi);
- }
- else
- {
- unsafe
- {
- Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
- Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));
- IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double));
- double* rColCache = (double*)rColCacheIP.ToPointer();
- // Precompute and then cache the value of R() for each column
- for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
- {
- double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
- double srcColumnFloor = Math.Floor(srcColumn);
- double srcColumnFrac = srcColumn - srcColumnFloor;
- int srcColumnInt = (int)srcColumn;
- for (int m = -1; m <= 2; ++m)
- {
- int index = (m + 1) + ((dstX - roi.Left) * 4);
- double x = m - srcColumnFrac;
- rColCache[index] = R(x);
- }
- }
- // Set this up so we can cache the R()'s for every row
- double* rRowCache = stackalloc double[4];
- for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
- {
- double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1);
- double srcRowFloor = (double)Math.Floor(srcRow);
- double srcRowFrac = srcRow - srcRowFloor;
- int srcRowInt = (int)srcRow;
- ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
- // Compute the R() values for this row
- for (int n = -1; n <= 2; ++n)
- {
- double x = srcRowFrac - n;
- rRowCache[n + 1] = R(x);
- }
- // See Perf Note below
- //int nFirst = Math.Max(-srcRowInt, -1);
- //int nLast = Math.Min(source.height - srcRowInt - 1, 2);
- for (int dstX = roi.Left; dstX < roi.Right; dstX++)
- {
- double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
- double srcColumnFloor = Math.Floor(srcColumn);
- double srcColumnFrac = srcColumn - srcColumnFloor;
- int srcColumnInt = (int)srcColumn;
- double blueSum = 0;
- double greenSum = 0;
- double redSum = 0;
- double alphaSum = 0;
- double totalWeight = 0;
- // See Perf Note below
- //int mFirst = Math.Max(-srcColumnInt, -1);
- //int mLast = Math.Min(source.width - srcColumnInt - 1, 2);
- ColorBgra* srcPtr = source.GetPointAddressUnchecked(srcColumnInt - 1, srcRowInt - 1);
- for (int n = -1; n <= 2; ++n)
- {
- int srcY = srcRowInt + n;
- for (int m = -1; m <= 2; ++m)
- {
- // Perf Note: It actually benchmarks faster on my system to do
- // a bounds check for every (m,n) than it is to limit the loop
- // to nFirst-Last and mFirst-mLast.
- // I'm leaving the code above, albeit commented out, so that
- // benchmarking between these two can still be performed.
- if (source.IsVisible(srcColumnInt + m, srcY))
- {
- double w0 = rColCache[(m + 1) + (4 * (dstX - roi.Left))];
- double w1 = rRowCache[n + 1];
- double w = w0 * w1;
- blueSum += srcPtr->B * w * srcPtr->A;
- greenSum += srcPtr->G * w * srcPtr->A;
- redSum += srcPtr->R * w * srcPtr->A;
- alphaSum += srcPtr->A * w;
- totalWeight += w;
- }
- ++srcPtr;
- }
- srcPtr = (ColorBgra*)((byte*)(srcPtr - 4) + source.stride);
- }
- double alpha = alphaSum / totalWeight;
- double blue;
- double green;
- double red;
- if (alpha == 0)
- {
- blue = 0;
- green = 0;
- red = 0;
- }
- else
- {
- blue = blueSum / alphaSum;
- green = greenSum / alphaSum;
- red = redSum / alphaSum;
- // add 0.5 to ensure truncation to uint results in rounding
- alpha += 0.5;
- blue += 0.5;
- green += 0.5;
- red += 0.5;
- }
- dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
- ++dstPtr;
- } // for (dstX...
- } // for (dstY...
- Memory.Free(rColCacheIP);
- } // unsafe
- }
- }
- /// <summary>
- /// Implements bicubic filtering with NO bounds checking at any pixel.
- /// </summary>
- public void BicubicFitSurfaceUnchecked(Surface source, Rectangle dstRoi)
- {
- if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2)
- {
- SuperSamplingFitSurface(source, dstRoi);
- }
- else
- {
- unsafe
- {
- Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
- Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));
- IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double));
- double* rColCache = (double*)rColCacheIP.ToPointer();
- // Precompute and then cache the value of R() for each column
- for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
- {
- double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
- double srcColumnFloor = Math.Floor(srcColumn);
- double srcColumnFrac = srcColumn - srcColumnFloor;
- int srcColumnInt = (int)srcColumn;
- for (int m = -1; m <= 2; ++m)
- {
- int index = (m + 1) + ((dstX - roi.Left) * 4);
- double x = m - srcColumnFrac;
- rColCache[index] = R(x);
- }
- }
- // Set this up so we can cache the R()'s for every row
- double* rRowCache = stackalloc double[4];
- for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
- {
- double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1);
- double srcRowFloor = Math.Floor(srcRow);
- double srcRowFrac = srcRow - srcRowFloor;
- int srcRowInt = (int)srcRow;
- ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
- // Compute the R() values for this row
- for (int n = -1; n <= 2; ++n)
- {
- double x = srcRowFrac - n;
- rRowCache[n + 1] = R(x);
- }
- rColCache = (double*)rColCacheIP.ToPointer();
- ColorBgra* srcRowPtr = source.GetRowAddressUnchecked(srcRowInt - 1);
- for (int dstX = roi.Left; dstX < roi.Right; dstX++)
- {
- double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
- double srcColumnFloor = Math.Floor(srcColumn);
- double srcColumnFrac = srcColumn - srcColumnFloor;
- int srcColumnInt = (int)srcColumn;
- double blueSum = 0;
- double greenSum = 0;
- double redSum = 0;
- double alphaSum = 0;
- double totalWeight = 0;
- ColorBgra* srcPtr = srcRowPtr + srcColumnInt - 1;
- for (int n = 0; n <= 3; ++n)
- {
- double w0 = rColCache[0] * rRowCache[n];
- double w1 = rColCache[1] * rRowCache[n];
- double w2 = rColCache[2] * rRowCache[n];
- double w3 = rColCache[3] * rRowCache[n];
- double a0 = srcPtr[0].A;
- double a1 = srcPtr[1].A;
- double a2 = srcPtr[2].A;
- double a3 = srcPtr[3].A;
- alphaSum += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3);
- totalWeight += w0 + w1 + w2 + w3;
- blueSum += (a0 * srcPtr[0].B * w0) + (a1 * srcPtr[1].B * w1) + (a2 * srcPtr[2].B * w2) + (a3 * srcPtr[3].B * w3);
- greenSum += (a0 * srcPtr[0].G * w0) + (a1 * srcPtr[1].G * w1) + (a2 * srcPtr[2].G * w2) + (a3 * srcPtr[3].G * w3);
- redSum += (a0 * srcPtr[0].R * w0) + (a1 * srcPtr[1].R * w1) + (a2 * srcPtr[2].R * w2) + (a3 * srcPtr[3].R * w3);
- srcPtr = (ColorBgra*)((byte*)srcPtr + source.stride);
- }
- double alpha = alphaSum / totalWeight;
- double blue;
- double green;
- double red;
- if (alpha == 0)
- {
- blue = 0;
- green = 0;
- red = 0;
- }
- else
- {
- blue = blueSum / alphaSum;
- green = greenSum / alphaSum;
- red = redSum / alphaSum;
- // add 0.5 to ensure truncation to uint results in rounding
- alpha += 0.5;
- blue += 0.5;
- green += 0.5;
- red += 0.5;
- }
- dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
- ++dstPtr;
- rColCache += 4;
- } // for (dstX...
- } // for (dstY...
- Memory.Free(rColCacheIP);
- } // unsafe
- }
- }
- /// <summary>
- /// Releases all resources held by this Surface 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.IsDisposed)
- {
- mat.Dispose();
- mat = null;
- }
- }
- }
- }
- ///<summary>
- /// 生成缩略图
- /// </summary>
- /// <param name="mat">源图片</param>
- /// <param name="width">缩略图宽度</param>
- /// <param name="height">缩略图高度</param>
- /// <param name="mode">生成缩略图的方式</param>
- public static OpenCvSharp.Mat MakeThumbnailMat(OpenCvSharp.Mat mat, int width, int height, string mode)
- {
- int towidth = width;
- int toheight = height;
- switch (mode)
- {
- case "HW"://指定高宽缩放(可能变形)
- break;
- case "W"://指定宽,高按比例
- toheight = mat.Height * width / mat.Width;
- break;
- case "H"://指定高,宽按比例
- towidth = mat.Width * height / mat.Height;
- break;
- default:
- break;
- }
- OpenCvSharp.Mat temp = new OpenCvSharp.Mat();
- OpenCvSharp.Cv2.Resize(mat, temp, new OpenCvSharp.Size(towidth, toheight));
- return temp;
- }
- ///<summary>
- /// 生成缩略图
- /// </summary>
- /// <param name="originalImage">源图片</param>
- /// <param name="width">缩略图宽度</param>
- /// <param name="height">缩略图高度</param>
- /// <param name="mode">生成缩略图的方式</param>
- public static Bitmap MakeThumbnail(/*Image originalImage*/OpenCvSharp.Mat mat, int width, int height, string mode)
- {
- int towidth = width;
- int toheight = height;
- int x = 0;
- int y = 0;
- int ow = mat.Width;
- int oh = mat.Height;
- switch (mode)
- {
- case "HW"://指定高宽缩放(可能变形)
- break;
- case "W"://指定宽,高按比例
- toheight = mat.Height * width / mat.Width;
- break;
- case "H"://指定高,宽按比例
- towidth = mat.Width * height / mat.Height;
- break;
- case "Cut"://指定高宽裁减(不变形)
- if ((double)mat.Width / (double)mat.Height > (double)towidth / (double)toheight)
- {
- oh = mat.Height;
- ow = mat.Height * towidth / toheight;
- y = 0;
- x = (mat.Width - ow) / 2;
- }
- else
- {
- ow = mat.Width;
- oh = mat.Width * height / towidth;
- x = 0;
- y = (mat.Height - oh) / 2;
- }
- break;
- default:
- break;
- }
- OpenCvSharp.Mat temp = new OpenCvSharp.Mat();
- OpenCvSharp.Cv2.Resize(mat, temp, new OpenCvSharp.Size(towidth, toheight));
- return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(temp);
- /*//新建一个bmp图片
- Bitmap bitmap = new System.Drawing.Bitmap(towidth, toheight);
- //新建一个画板
- Graphics g = System.Drawing.Graphics.FromImage(bitmap);
- //设置高质量插值法
- g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
- //设置高质量,低速度呈现平滑程度
- g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
- //清空画布并以透明背景色填充
- g.Clear(Color.Transparent);
- //在指定位置并且按指定大小绘制原图片的指定部分
- g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight),
- new Rectangle(x, y, ow, oh),
- GraphicsUnit.Pixel);
- return bitmap;*/
- }
- public static Bitmap MakeThumbnail(Image originalImage, int width, int height, string mode)
- {
- int towidth = width;
- int toheight = height;
- int x = 0;
- int y = 0;
- int ow = originalImage.Width;
- int oh = originalImage.Height;
- switch (mode)
- {
- case "HW"://指定高宽缩放(可能变形)
- break;
- case "W"://指定宽,高按比例
- toheight = originalImage.Height * width / originalImage.Width;
- break;
- case "H"://指定高,宽按比例
- towidth = originalImage.Width * height / originalImage.Height;
- break;
- case "Cut"://指定高宽裁减(不变形)
- if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
- {
- oh = originalImage.Height;
- ow = originalImage.Height * towidth / toheight;
- y = 0;
- x = (originalImage.Width - ow) / 2;
- }
- else
- {
- ow = originalImage.Width;
- oh = originalImage.Width * height / towidth;
- x = 0;
- y = (originalImage.Height - oh) / 2;
- }
- break;
- default:
- break;
- }
- //新建一个bmp图片
- Bitmap bitmap = new System.Drawing.Bitmap(towidth, toheight);
- //新建一个画板
- Graphics g = System.Drawing.Graphics.FromImage(bitmap);
- //设置高质量插值法
- g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
- //设置高质量,低速度呈现平滑程度
- g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
- //清空画布并以透明背景色填充
- g.Clear(Color.Transparent);
- //在指定位置并且按指定大小绘制原图片的指定部分
- g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight),
- new Rectangle(x, y, ow, oh),
- GraphicsUnit.Pixel);
- return bitmap;
- }
- }
- }
|