using System; using System.Drawing; using System.Windows.Forms; namespace PaintDotNet { public sealed class StitchSurfaceBoxRendererList { public StitchSurfaceBoxRenderer[] list; public StitchSurfaceBoxRenderer[] topList; private Size sourceSize; private Size destinationSize; private ScaleFactor scaleFactor; // ratio is dst:src private object lockObject = new object(); public object SyncRoot { get { return this.lockObject; } } public ScaleFactor ScaleFactor { get { return this.scaleFactor; } } public StitchSurfaceBoxRenderer[][] Renderers { get { return new StitchSurfaceBoxRenderer[][] { list, topList }; } } private void ComputeScaleFactor() { scaleFactor = new ScaleFactor(this.DestinationSize.Width, this.SourceSize.Width); } public Point SourceToDestination(Point pt) { return this.scaleFactor.ScalePoint(pt); } public RectangleF SourceToDestination(Rectangle rect) { return this.scaleFactor.ScaleRectangle((RectangleF)rect); } public Point DestinationToSource(Point pt) { return this.scaleFactor.UnscalePoint(pt); } public void Add(StitchSurfaceBoxRenderer addMe, bool alwaysOnTop) { StitchSurfaceBoxRenderer[] startList = alwaysOnTop ? this.topList : this.list; StitchSurfaceBoxRenderer[] listPlusOne = new StitchSurfaceBoxRenderer[startList.Length + 1]; for (int i = 0; i < startList.Length; ++i) { listPlusOne[i] = startList[i]; } listPlusOne[listPlusOne.Length - 1] = addMe; if (alwaysOnTop) { this.topList = listPlusOne; } else { this.list = listPlusOne; } Invalidate(); } public void Remove(StitchSurfaceBoxRenderer removeMe) { if (this.list.Length == 0 && this.topList.Length == 0) { throw new InvalidOperationException("zero items left, can't remove anything"); } else { bool found = false; if (this.list.Length > 0) { StitchSurfaceBoxRenderer[] listSubOne = new StitchSurfaceBoxRenderer[this.list.Length - 1]; bool foundHere = false; int dstIndex = 0; for (int i = 0; i < this.list.Length; ++i) { if (this.list[i] == removeMe) { if (foundHere) { throw new ArgumentException("removeMe appeared multiple times in the list"); } else { foundHere = true; } } else { if (dstIndex == this.list.Length - 1) { // was not found } else { listSubOne[dstIndex] = this.list[i]; ++dstIndex; } } } if (foundHere) { this.list = listSubOne; found = true; } } if (this.topList.Length > 0) { StitchSurfaceBoxRenderer[] topListSubOne = new StitchSurfaceBoxRenderer[this.topList.Length - 1]; int topDstIndex = 0; bool foundHere = false; for (int i = 0; i < this.topList.Length; ++i) { if (this.topList[i] == removeMe) { if (found || foundHere) { throw new ArgumentException("removeMe appeared multiple times in the list"); } else { foundHere = true; } } else { if (topDstIndex == this.topList.Length - 1) { // was not found } else { topListSubOne[topDstIndex] = this.topList[i]; ++topDstIndex; } } } if (foundHere) { this.topList = topListSubOne; found = true; } } if (!found) { throw new ArgumentException("removeMe was not found", "removeMe"); } Invalidate(); } } private void OnDestinationSizeChanged() { InvalidateLookups(); if (this.destinationSize.Width != 0 && this.sourceSize.Width != 0) { ComputeScaleFactor(); for (int i = 0; i < this.list.Length; ++i) { this.list[i].OnDestinationSizeChanged(); } for (int i = 0; i < this.topList.Length; ++i) { this.topList[i].OnDestinationSizeChanged(); } } } public Size DestinationSize { get { return this.destinationSize; } set { if (this.destinationSize != value) { this.destinationSize = value; OnDestinationSizeChanged(); } } } private void OnSourceSizeChanged() { InvalidateLookups(); if (this.destinationSize.Width != 0 && this.sourceSize.Width != 0) { ComputeScaleFactor(); for (int i = 0; i < this.list.Length; ++i) { this.list[i].OnSourceSizeChanged(); } for (int i = 0; i < this.topList.Length; ++i) { this.topList[i].OnSourceSizeChanged(); } } } public Size SourceSize { get { return this.sourceSize; } set { if (this.sourceSize != value) { this.sourceSize = value; OnSourceSizeChanged(); } } } public int[] Dst2SrcLookupX { get { lock (this.SyncRoot) { CreateD2SLookupX(); } return this.d2SLookupX; } } private int[] d2SLookupX; // maps from destination->source coordinates private void CreateD2SLookupX() { if (this.d2SLookupX == null || this.d2SLookupX.Length != this.DestinationSize.Width + 1) { this.d2SLookupX = new int[this.DestinationSize.Width + 1]; for (int x = 0; x < d2SLookupX.Length; ++x) { Point pt = new Point(x, 0); Point surfacePt = this.DestinationToSource(pt); // Sometimes the scale factor is slightly different on one axis than // on another, simply due to accuracy. So we have to clamp this value to // be within bounds. d2SLookupX[x] = Utility.Clamp(surfacePt.X, 0, this.SourceSize.Width - 1); } } } public int[] Dst2SrcLookupY { get { lock (this.SyncRoot) { CreateD2SLookupY(); } return this.d2SLookupY; } } private int[] d2SLookupY; // maps from destination->source coordinates private void CreateD2SLookupY() { if (this.d2SLookupY == null || this.d2SLookupY.Length != this.DestinationSize.Height + 1) { this.d2SLookupY = new int[this.DestinationSize.Height + 1]; for (int y = 0; y < d2SLookupY.Length; ++y) { Point pt = new Point(0, y); Point surfacePt = this.DestinationToSource(pt); // Sometimes the scale factor is slightly different on one axis than // on another, simply due to accuracy. So we have to clamp this value to // be within bounds. d2SLookupY[y] = Utility.Clamp(surfacePt.Y, 0, this.SourceSize.Height - 1); } } } public int[] Src2DstLookupX { get { lock (this.SyncRoot) { CreateS2DLookupX(); } return this.s2DLookupX; } } private int[] s2DLookupX; // maps from source->destination coordinates private void CreateS2DLookupX() { if (this.s2DLookupX == null || this.s2DLookupX.Length != this.SourceSize.Width + 1) { this.s2DLookupX = new int[this.SourceSize.Width + 1]; for (int x = 0; x < s2DLookupX.Length; ++x) { Point pt = new Point(x, 0); Point clientPt = this.SourceToDestination(pt); // Sometimes the scale factor is slightly different on one axis than // on another, simply due to accuracy. So we have to clamp this value to // be within bounds. s2DLookupX[x] = Utility.Clamp(clientPt.X, 0, this.DestinationSize.Width - 1); } } } public int[] Src2DstLookupY { get { lock (this.SyncRoot) { CreateS2DLookupY(); } return this.s2DLookupY; } } private int[] s2DLookupY; // maps from source->destination coordinates private void CreateS2DLookupY() { if (this.s2DLookupY == null || this.s2DLookupY.Length != this.SourceSize.Height + 1) { this.s2DLookupY = new int[this.SourceSize.Height + 1]; for (int y = 0; y < s2DLookupY.Length; ++y) { Point pt = new Point(0, y); Point clientPt = this.SourceToDestination(pt); // Sometimes the scale factor is slightly different on one axis than // on another, simply due to accuracy. So we have to clamp this value to // be within bounds. s2DLookupY[y] = Utility.Clamp(clientPt.Y, 0, this.DestinationSize.Height - 1); } } } public void InvalidateLookups() { this.s2DLookupX = null; this.s2DLookupY = null; this.d2SLookupX = null; this.d2SLookupY = null; } public void Render(StitchSurface dst, Point offset) { foreach (StitchSurfaceBoxRenderer sbr in this.list) { if (sbr.Visible) { sbr.Render(dst, offset); } } foreach (StitchSurfaceBoxRenderer sbr in this.topList) { if (sbr.Visible) { sbr.Render(dst, offset); } } } public void RenderTop(StitchSurface dst, Point offset) { foreach (StitchSurfaceBoxRenderer sbr in this.topList) { if (sbr.Visible) { sbr.Render(dst, offset); } } } public event InvalidateEventHandler Invalidated; public void Invalidate(Rectangle rect) { if (Invalidated != null) { Invalidated(this, new InvalidateEventArgs(rect)); } } public void Invalidate() { Invalidate(StitchSurfaceBoxRenderer.MaxBounds); } public StitchSurfaceBoxRendererList(Size sourceSize, Size destinationSize) { this.list = new StitchSurfaceBoxRenderer[0]; this.topList = new StitchSurfaceBoxRenderer[0]; this.sourceSize = sourceSize; this.destinationSize = destinationSize; } } }