123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649 |
- using System;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Runtime.Serialization;
- namespace PaintDotNet
- {
- /// <summary>
- /// Designed as a proxy to the GDI+ Region class, while allowing for a
- /// replacement that won't break code. The main reason for having this
- /// right now is to work around some bugs in System.Drawing.Region,
- /// especially the memory leak in GetRegionScans().
- /// </summary>
- [Serializable]
- public sealed class PdnRegion : ISerializable, IDisposable
- {
- private object lockObject = new object();
- private Region gdiRegion;
- private bool changed = true;
- private int cachedArea = -1;
- private Rectangle cachedBounds = Rectangle.Empty;
- private RectangleF[] cachedRectsF = null;
- private Rectangle[] cachedRects = null;
- public object SyncRoot
- {
- get
- {
- return lockObject;
- }
- }
- public int GetArea()
- {
- lock (SyncRoot)
- {
- int theCachedArea = cachedArea;
- if (theCachedArea == -1)
- {
- int ourCachedArea = 0;
- foreach (Rectangle rect in GetRegionScansReadOnlyInt())
- {
- try
- {
- ourCachedArea += rect.Width * rect.Height;
- }
- catch (System.OverflowException)
- {
- ourCachedArea = int.MaxValue;
- break;
- }
- }
- cachedArea = ourCachedArea;
- return ourCachedArea;
- }
- else
- {
- return theCachedArea;
- }
- }
- }
- private bool IsChanged()
- {
- return this.changed;
- }
- private void Changed()
- {
- lock (SyncRoot)
- {
- this.changed = true;
- this.cachedArea = -1;
- this.cachedBounds = Rectangle.Empty;
- }
- }
- private void ResetChanged()
- {
- lock (SyncRoot)
- {
- this.changed = false;
- }
- }
- public PdnRegion()
- {
- this.gdiRegion = new Region();
- }
- public PdnRegion(GraphicsPath path)
- {
- this.gdiRegion = new Region(path);
- }
- public PdnRegion(Rectangle rect)
- {
- this.gdiRegion = new Region(rect);
- }
- public PdnRegion(RectangleF rectF)
- {
- this.gdiRegion = new Region(rectF);
- }
- public PdnRegion(RegionData regionData)
- {
- this.gdiRegion = new Region(regionData);
- }
- public PdnRegion(Region region, bool takeOwnership)
- {
- if (takeOwnership)
- {
- this.gdiRegion = region;
- }
- else
- {
- this.gdiRegion = region.Clone();
- }
- }
- public PdnRegion(Region region)
- : this(region, false)
- {
- }
- private PdnRegion(PdnRegion pdnRegion)
- {
- lock (pdnRegion.SyncRoot)
- {
- this.gdiRegion = pdnRegion.gdiRegion.Clone();
- this.changed = pdnRegion.changed;
- this.cachedArea = pdnRegion.cachedArea;
- this.cachedRectsF = pdnRegion.cachedRectsF;
- this.cachedRects = pdnRegion.cachedRects;
- }
- }
- // This constructor is used by WrapRegion. The boolean parameter is just
- // there because we already have a parameterless contructor
- private PdnRegion(bool sentinel)
- {
- }
- public static PdnRegion CreateEmpty()
- {
- PdnRegion region = new PdnRegion();
- region.MakeEmpty();
- return region;
- }
- public static PdnRegion WrapRegion(Region region)
- {
- PdnRegion pdnRegion = new PdnRegion(false);
- pdnRegion.gdiRegion = region;
- return pdnRegion;
- }
- public void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (this.disposed)
- {
- throw new ObjectDisposedException("PdnRegion");
- }
- RegionData regionData;
- lock (SyncRoot)
- {
- regionData = this.gdiRegion.GetRegionData();
- }
- byte[] data = regionData.Data;
- info.AddValue("data", data);
- }
- public PdnRegion(SerializationInfo info, StreamingContext context)
- {
- byte[] data = (byte[])info.GetValue("data", typeof(byte[]));
- using (Region region = new Region())
- {
- RegionData regionData = region.GetRegionData();
- regionData.Data = data;
- this.gdiRegion = new Region(regionData);
- }
- this.lockObject = new object();
- this.cachedArea = -1;
- this.cachedBounds = Rectangle.Empty;
- this.changed = true;
- this.cachedRects = null;
- this.cachedRectsF = null;
- }
- public PdnRegion Clone()
- {
- return new PdnRegion(this);
- }
- ~PdnRegion()
- {
- Dispose(false);
- }
- private bool disposed = false;
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- private void Dispose(bool disposing)
- {
- if (!disposed)
- {
- if (disposing)
- {
- lock (SyncRoot)
- {
- gdiRegion.Dispose();
- gdiRegion = null;
- }
- }
- disposed = true;
- }
- }
- public Region GetRegionReadOnly()
- {
- return this.gdiRegion;
- }
- public void Complement(GraphicsPath path)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Complement(path);
- }
- }
- public void Complement(Rectangle rect)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Complement(rect);
- }
- }
- public void Complement(RectangleF rectF)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Complement(rectF);
- }
- }
- public void Complement(Region region)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Complement(region);
- }
- }
- public void Complement(PdnRegion region2)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Complement(region2.gdiRegion);
- }
- }
- public void Exclude(GraphicsPath path)
- {
- lock (SyncRoot)
- {
- gdiRegion.Exclude(path);
- }
- }
- public void Exclude(Rectangle rect)
- {
- lock (SyncRoot)
- {
- gdiRegion.Exclude(rect);
- }
- }
- public void Exclude(RectangleF rectF)
- {
- lock (SyncRoot)
- {
- gdiRegion.Exclude(rectF);
- }
- }
- public void Exclude(Region region)
- {
- lock (SyncRoot)
- {
- gdiRegion.Exclude(region);
- }
- }
- public void Exclude(PdnRegion region2)
- {
- lock (SyncRoot)
- {
- gdiRegion.Exclude(region2.gdiRegion);
- }
- }
- public RectangleF GetBounds(Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.GetBounds(graphics);
- }
- }
- public RectangleF GetBounds()
- {
- lock (SyncRoot)
- {
- using (SystemLayer.NullGraphics nullGraphics = new SystemLayer.NullGraphics())
- {
- return gdiRegion.GetBounds(nullGraphics.Graphics);
- }
- }
- }
- public Rectangle GetBoundsInt()
- {
- Rectangle bounds;
- lock (SyncRoot)
- {
- bounds = this.cachedBounds;
- if (bounds == Rectangle.Empty)
- {
- Rectangle[] rects = GetRegionScansReadOnlyInt();
- if (rects.Length == 0)
- {
- return Rectangle.Empty;
- }
- bounds = rects[0];
- for (int i = 1; i < rects.Length; ++i)
- {
- bounds = Rectangle.Union(bounds, rects[i]);
- }
- this.cachedBounds = bounds;
- }
- }
- return bounds;
- }
- public RegionData GetRegionData()
- {
- lock (SyncRoot)
- {
- return gdiRegion.GetRegionData();
- }
- }
- public RectangleF[] GetRegionScans()
- {
- return (RectangleF[])GetRegionScansReadOnly().Clone();
- }
- /// <summary>
- /// This is an optimized version of GetRegionScans that returns a reference to the array
- /// that is used to cache the region scans. This mitigates performance when this array
- /// is requested many times on an unmodified PdnRegion.
- /// Thus, by using this method you are promising to not modify the array that is returned.
- /// </summary>
- /// <returns></returns>
- public RectangleF[] GetRegionScansReadOnly()
- {
- lock (this.SyncRoot)
- {
- if (this.changed)
- {
- UpdateCachedRegionScans();
- }
- if (this.cachedRectsF == null)
- {
- this.cachedRectsF = new RectangleF[cachedRects.Length];
- for (int i = 0; i < this.cachedRectsF.Length; ++i)
- {
- this.cachedRectsF[i] = (RectangleF)this.cachedRects[i];
- }
- }
- return this.cachedRectsF;
- }
- }
- public Rectangle[] GetRegionScansInt()
- {
- return (Rectangle[])GetRegionScansReadOnlyInt().Clone();
- }
- public Rectangle[] GetRegionScansReadOnlyInt()
- {
- lock (this.SyncRoot)
- {
- if (this.changed)
- {
- UpdateCachedRegionScans();
- }
- return this.cachedRects;
- }
- }
- private unsafe void UpdateCachedRegionScans()
- {
- // Assumes we are in a lock(SyncRoot){} block
- SystemLayer.PdnGraphics.GetRegionScans(this.gdiRegion, out cachedRects, out cachedArea);
- this.cachedRectsF = null; // only update this when specifically asked for it
- }
- public void Intersect(GraphicsPath path)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Intersect(path);
- }
- }
- public void Intersect(Rectangle rect)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Intersect(rect);
- }
- }
- public void Intersect(RectangleF rectF)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Intersect(rectF);
- }
- }
- public void Intersect(Region region)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Intersect(region);
- }
- }
- public void Intersect(PdnRegion region2)
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.Intersect(region2.gdiRegion);
- }
- }
- public bool IsEmpty(Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsEmpty(graphics);
- }
- }
- public bool IsEmpty()
- {
- return GetArea() == 0;
- }
- public bool IsInfinite(Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsInfinite(graphics);
- }
- }
- public bool IsVisible(Point point)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(point);
- }
- }
- public bool IsVisible(PointF pointF)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(pointF);
- }
- }
- public bool IsVisible(Rectangle rect)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(rect);
- }
- }
- public bool IsVisible(RectangleF rectF)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(rectF);
- }
- }
- public bool IsVisible(Point point, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(point, graphics);
- }
- }
- public bool IsVisible(PointF pointF, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(pointF, graphics);
- }
- }
- public bool IsVisible(Rectangle rect, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(rect, graphics);
- }
- }
- public bool IsVisible(RectangleF rectF, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(rectF, graphics);
- }
- }
- public bool IsVisible(float x, float y)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y);
- }
- }
- public bool IsVisible(int x, int y, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, graphics);
- }
- }
- public bool IsVisible(float x, float y, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, graphics);
- }
- }
- public bool IsVisible(int x, int y, int width, int height)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, width, height);
- }
- }
- public bool IsVisible(float x, float y, float width, float height)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, width, height);
- }
- }
- public bool IsVisible(int x, int y, int width, int height, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, width, height, graphics);
- }
- }
- public bool IsVisible(float x, float y, float width, float height, Graphics graphics)
- {
- lock (SyncRoot)
- {
- return gdiRegion.IsVisible(x, y, width, height, graphics);
- }
- }
- public void MakeEmpty()
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.MakeEmpty();
- }
- }
- public void MakeInfinite()
- {
- lock (SyncRoot)
- {
- Changed();
- gdiRegion.MakeInfinite();
- }
- }
- }
- }
|