private Rectangle[][] SliceUpRegion(PdnRegion region, int sliceCount, Rectangle layerBounds) { if (sliceCount <= 0) { throw new ArgumentOutOfRangeException("sliceCount"); } Rectangle[][] rectangleArray = new Rectangle[sliceCount][]; Scanline[] regionScanlines = GetRegionScanlines(region.GetRegionScansReadOnlyInt()); for (int i = 0; i < sliceCount; i++) { int num2 = (regionScanlines.Length * i) / sliceCount; int num3 = Math.Min(regionScanlines.Length, (regionScanlines.Length * (i + 1)) / sliceCount); if (sliceCount > 1) { switch (i) { case 0: num3 = Math.Min(num3, num2 + 1); break; case 1: num2 = Math.Min(num2, 1); break; } } Rectangle[] scans = ScanlinesToRectangles(regionScanlines, num2, num3 - num2); for (int j = 0; j < scans.Length; j++) { scans[j].Intersect(layerBounds); } rectangleArray[i] = this.ConsolidateRects(scans); } return(rectangleArray); }
private Rectangle[][] SliceUpRegion(PdnRegion region, int sliceCount, Rectangle layerBounds) { Rectangle[][] slices = new Rectangle[sliceCount][]; Rectangle[] regionRects = region.GetRegionScansReadOnlyInt(); Scanline[] regionScans = Utility.GetRegionScans(regionRects); for (int i = 0; i < sliceCount; ++i) { int beginScan = (regionScans.Length * i) / sliceCount; int endScan = Math.Min(regionScans.Length, (regionScans.Length * (i + 1)) / sliceCount); // Try to arrange it such that the maximum size of the first region // is 1-pixel tall if (i == 0) { endScan = Math.Min(endScan, beginScan + 1); } else if (i == 1) { beginScan = Math.Min(beginScan, 1); } Rectangle[] newRects = Utility.ScanlinesToRectangles(regionScans, beginScan, endScan - beginScan); for (int j = 0; j < newRects.Length; ++j) { newRects[j].Intersect(layerBounds); } slices[i] = newRects; } return(slices); }
/// <summary> /// This is a helper function. It allows you to render an effect "in place." /// That is, you don't need both a destination and a source Surface. /// </summary> public void RenderInPlace(RenderArgs srcAndDstArgs, PdnRegion roi) { using (Surface renderSurface = new Surface(srcAndDstArgs.Surface.Size)) { using (RenderArgs renderArgs = new RenderArgs(renderSurface)) { Rectangle[] scans = roi.GetRegionScansReadOnlyInt(); Render(null, renderArgs, srcAndDstArgs, scans); srcAndDstArgs.Surface.CopySurface(renderSurface, roi); } } }
private void RenderGradient(Surface surface, PdnRegion clipRegion, CompositingMode compositingMode, PointF startPointF, ColorBgra startColor, PointF endPointF, ColorBgra endColor) { GradientRenderer gr = AppEnvironment.GradientInfo.CreateGradientRenderer(); gr.StartColor = startColor; gr.EndColor = endColor; gr.StartPoint = startPointF; gr.EndPoint = endPointF; gr.AlphaBlending = (compositingMode == CompositingMode.SourceOver); gr.BeforeRender(); Rectangle[] oldRois = clipRegion.GetRegionScansReadOnlyInt(); Rectangle[] newRois; if (oldRois.Length == 1) { newRois = new Rectangle[Processor.LogicalCpuCount]; Utility.SplitRectangle(oldRois[0], newRois); } else { newRois = oldRois; } RenderContext rc = new RenderContext(); rc.surface = surface; rc.rois = newRois; rc.renderer = gr; WaitCallback wc = new WaitCallback(rc.Render); for (int i = 0; i < Processor.LogicalCpuCount; ++i) { if (i == Processor.LogicalCpuCount - 1) { wc(BoxedConstants.GetInt32(i)); } else { PaintDotNet.Threading.ThreadPool.Global.QueueUserWorkItem(wc, BoxedConstants.GetInt32(i)); } } PaintDotNet.Threading.ThreadPool.Global.Drain(); }
private Rectangle[][] SliceUpRegion(PdnRegion region, int sliceCount, Rectangle layerBounds) { if (sliceCount <= 0) { throw new ArgumentOutOfRangeException("sliceCount"); } Rectangle[][] slices = new Rectangle[sliceCount][]; Rectangle[] regionRects = region.GetRegionScansReadOnlyInt(); Scanline[] regionScans = GetRegionScanlines(regionRects); for (int i = 0; i < sliceCount; ++i) { int beginScan = (regionScans.Length * i) / sliceCount; int endScan = Math.Min(regionScans.Length, (regionScans.Length * (i + 1)) / sliceCount); // Try to arrange it such that the maximum size of the first region is 1-pixel tall if (sliceCount > 1) { if (i == 0) { endScan = Math.Min(endScan, beginScan + 1); } else if (i == 1) { beginScan = Math.Min(beginScan, 1); } } Rectangle[] newRects = ScanlinesToRectangles(regionScans, beginScan, endScan - beginScan); for (int j = 0; j < newRects.Length; ++j) { newRects[j].Intersect(layerBounds); } Rectangle[] consRects = ConsolidateRects(newRects); slices[i] = consRects; } return(slices); }
private unsafe void DrawOverPoints(Point start, Point finish, ColorBgra colorToReplaceWith, ColorBgra colorBeingReplaced) { ColorBgra colorAdjusted = ColorBgra.FromColor(Color.Empty); byte dstAlpha; ColorBgra colorLifted; Rectangle[] rectSelRegions; Rectangle rectBrushArea; Rectangle rectBrushRelativeOffset = new Rectangle(0, 0, 0, 0); // special condition for a canvas with no active selection // create an array of rectangles with a single rectangle // specifying the size of the canvas if (Selection.IsEmpty) { rectSelRegions = new Rectangle[] { DocumentWorkspace.Document.Bounds }; } else { rectSelRegions = clipRegion.GetRegionScansReadOnlyInt(); } // code ripped off from clone stamp tool Point direction = new Point(finish.X - start.X, finish.Y - start.Y); float length = Utility.Magnitude(direction); float bw = AppEnvironment.PenInfo.Width / 2; float fInc; if (length == 0.0f) { fInc = float.PositiveInfinity; } else { fInc = (float)Math.Sqrt(bw) / length; } // iterate through all points in the linear stroke for (float f = 0; f < 1; f += fInc) { PointF q = new PointF(finish.X * (1 - f) + f * start.X, finish.Y * (1 - f) + f * start.Y); Point p = Point.Round(q); // iterate through all rectangles foreach (Rectangle rectSel in rectSelRegions) { // set the perimeter values for the rectBrushRegion rectangle // so the area can be intersected with the active // selection individual recSelRegion rectangle. rectBrushArea = new Rectangle(p.X - halfPenWidth, p.Y - halfPenWidth, ceilingPenWidth, ceilingPenWidth); // test the intersection... // the perimeter values of rectBrushRegion (above) // may calculate negative but // *should* always be clipped to acceptable values by // by the following intersection. if (rectBrushArea.IntersectsWith(rectSel)) { // a valid intersection was found. // prune the brush rectangle to fit the intersection. rectBrushArea.Intersect(rectSel); for (int y = rectBrushArea.Top; y < rectBrushArea.Bottom; y++) { // create a new rectangle for an offset relative to the // the brush mask rectBrushRelativeOffset.X = Math.Max(rectSel.X - (p.X - halfPenWidth), 0); rectBrushRelativeOffset.Y = Math.Max(rectSel.Y - (p.Y - halfPenWidth), 0); rectBrushRelativeOffset.Size = rectBrushArea.Size; ColorBgra *srcBgra; ColorBgra *dstBgra; try { // get the source address of the first pixel from the brush mask. srcBgra = (ColorBgra *)brushRenderArgs.Surface.GetPointAddress(rectBrushRelativeOffset.Left, rectBrushRelativeOffset.Y + (y - rectBrushArea.Y)); // get the address of the pixel we want to change on the canvas. dstBgra = (ColorBgra *)renderArgs.Surface.GetPointAddress(rectBrushArea.Left, y); } catch { return; } for (int x = rectBrushArea.Left; x < rectBrushArea.Right; x++) { if (srcBgra->A != 0) { colorLifted = *dstBgra; // hasDrawn is set if a pixel endures color replacement so that // the placed surface will be left alone, otherwise, the placed // surface will be discarded // adjust the channel color up and down based on the difference calculated // from the source. These values are clamped to a byte. It's possible // that the new color is too dark or too bright to take the whole range bool boolCIT = this.IsColorInTolerance(colorLifted, colorBeingReplaced); bool boolPAAA = false; if (AppEnvironment.AntiAliasing) { boolPAAA = this.IsPointAlreadyAntiAliased(x, y); } if (boolCIT || boolPAAA) { if (boolPAAA) { colorAdjusted = (ColorBgra)AAPoints(x, y); if (penWidth < 2.0f) { colorAdjusted.B = Utility.ClampToByte(colorToReplaceWith.B + (colorAdjusted.B - colorBeingReplaced.B)); colorAdjusted.G = Utility.ClampToByte(colorToReplaceWith.G + (colorAdjusted.G - colorBeingReplaced.G)); colorAdjusted.R = Utility.ClampToByte(colorToReplaceWith.R + (colorAdjusted.R - colorBeingReplaced.R)); colorAdjusted.A = Utility.ClampToByte(colorToReplaceWith.A + (colorAdjusted.A - colorBeingReplaced.A)); } } else { colorAdjusted.B = Utility.ClampToByte(colorLifted.B + (colorToReplaceWith.B - colorBeingReplaced.B)); colorAdjusted.G = Utility.ClampToByte(colorLifted.G + (colorToReplaceWith.G - colorBeingReplaced.G)); colorAdjusted.R = Utility.ClampToByte(colorLifted.R + (colorToReplaceWith.R - colorBeingReplaced.R)); colorAdjusted.A = Utility.ClampToByte(colorLifted.A + (colorToReplaceWith.A - colorBeingReplaced.A)); } if ((srcBgra->A != 255) && AppEnvironment.AntiAliasing) { colorAdjusted.A = srcBgra->A; dstAlpha = dstBgra->A; *dstBgra = blendOp.Apply(*dstBgra, colorAdjusted); dstBgra->A = dstAlpha; if (!this.IsPointAlreadyAntiAliased(x, y)) { AAPointsAdd(x, y, colorAdjusted); } } else { colorAdjusted.A = (*dstBgra).A; *dstBgra = colorAdjusted; if (boolPAAA) { AAPointsRemove(x, y); } } hasDrawn = true; } } ++srcBgra; ++dstBgra; } } } } } }
public override HistoryMemento OnExecute(IHistoryWorkspace historyWorkspace) { if (historyWorkspace.Selection.IsEmpty) { return null; } else { PdnRegion selectionRegion = historyWorkspace.Selection.CreateRegion(); if (selectionRegion.GetArea() == 0) { selectionRegion.Dispose(); return null; } SelectionHistoryMemento sha = new SelectionHistoryMemento(StaticName, null, historyWorkspace); ReplaceDocumentHistoryMemento rdha = new ReplaceDocumentHistoryMemento(StaticName, null, historyWorkspace); Rectangle boundingBox; Rectangle[] inverseRegionRects = null; boundingBox = Utility.GetRegionBounds(selectionRegion); using (PdnRegion inverseRegion = new PdnRegion(boundingBox)) { inverseRegion.Exclude(selectionRegion); inverseRegionRects = Utility.TranslateRectangles( inverseRegion.GetRegionScansReadOnlyInt(), -boundingBox.X, -boundingBox.Y); } selectionRegion.Dispose(); selectionRegion = null; Document oldDocument = historyWorkspace.Document; // TODO: serialize this to disk so we don't *have* to store the full thing Document newDocument = new Document(boundingBox.Width, boundingBox.Height); // copy the document's meta data over newDocument.ReplaceMetaDataFrom(oldDocument); foreach (Layer layer in oldDocument.Layers) { if (layer is BitmapLayer) { BitmapLayer oldLayer = (BitmapLayer)layer; Surface croppedSurface = oldLayer.Surface.CreateWindow(boundingBox); BitmapLayer newLayer = new BitmapLayer(croppedSurface); ColorBgra clearWhite = ColorBgra.White.NewAlpha(0); foreach (Rectangle rect in inverseRegionRects) { newLayer.Surface.Clear(rect, clearWhite); } newLayer.LoadProperties(oldLayer.SaveProperties()); newDocument.Layers.Add(newLayer); } else { throw new InvalidOperationException("Crop does not support Layers that are not BitmapLayers"); } } CompoundHistoryMemento cha = new CompoundHistoryMemento( StaticName, PdnResources.GetImageResource("Icons.MenuImageCropIcon.png"), new HistoryMemento[] { sha, rdha }); EnterCriticalRegion(); historyWorkspace.Document = newDocument; return cha; } }
private static unsafe void LoadOrSaveSurfaceRegion(FileStream fileHandle, Surface surface, PdnRegion region, bool trueForSave) { Rectangle[] scans = region.GetRegionScansReadOnlyInt(); Rectangle regionBounds = region.GetBoundsInt(); Rectangle surfaceBounds = surface.Bounds; int scanCount = 0; void *[] ppvBuffers; uint[] lengths; regionBounds.Intersect(surfaceBounds); long length = (long)regionBounds.Width * (long)regionBounds.Height * (long)ColorBgra.SizeOf; if (scans.Length == 1 && length <= uint.MaxValue && surface.IsContiguousMemoryRegion(regionBounds)) { ppvBuffers = new void *[1]; lengths = new uint[1]; ppvBuffers[0] = surface.GetPointAddressUnchecked(regionBounds.Location); lengths[0] = (uint)length; } else { for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.Intersect(surfaceBounds); if (rect.Width != 0 && rect.Height != 0) { scanCount += rect.Height; } } int scanIndex = 0; ppvBuffers = new void *[scanCount]; lengths = new uint[scanCount]; for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.Intersect(surfaceBounds); if (rect.Width != 0 && rect.Height != 0) { for (int y = rect.Top; y < rect.Bottom; ++y) { ppvBuffers[scanIndex] = surface.GetPointAddressUnchecked(rect.Left, y); lengths[scanIndex] = (uint)(rect.Width * ColorBgra.SizeOf); ++scanIndex; } } } } if (trueForSave) { FileSystem.WriteToStreamingFileGather(fileHandle, ppvBuffers, lengths); } else { FileSystem.ReadFromStreamScatter(fileHandle, ppvBuffers, lengths); } }
private static unsafe void LoadOrSaveSurfaceRegion(FileStream fileHandle, Surface surface, PdnRegion region, bool trueForSave) { Rectangle[] scans = region.GetRegionScansReadOnlyInt(); Rectangle regionBounds = region.GetBoundsInt(); Rectangle surfaceBounds = surface.Bounds; int scanCount = 0; void*[] ppvBuffers; uint[] lengths; regionBounds.Intersect(surfaceBounds); long length = (long)regionBounds.Width * (long)regionBounds.Height * (long)ColorBgra.SizeOf; if (scans.Length == 1 && length <= uint.MaxValue && surface.IsContiguousMemoryRegion(regionBounds)) { ppvBuffers = new void*[1]; lengths = new uint[1]; ppvBuffers[0] = surface.GetPointAddressUnchecked(regionBounds.Location); lengths[0] = (uint)length; } else { for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.Intersect(surfaceBounds); if (rect.Width != 0 && rect.Height != 0) { scanCount += rect.Height; } } int scanIndex = 0; ppvBuffers = new void*[scanCount]; lengths = new uint[scanCount]; for (int i = 0; i < scans.Length; ++i) { Rectangle rect = scans[i]; rect.Intersect(surfaceBounds); if (rect.Width != 0 && rect.Height != 0) { for (int y = rect.Top; y < rect.Bottom; ++y) { ppvBuffers[scanIndex] = surface.GetPointAddressUnchecked(rect.Left, y); lengths[scanIndex] = (uint)(rect.Width * ColorBgra.SizeOf); ++scanIndex; } } } } if (trueForSave) { FileSystem.WriteToStreamingFileGather(fileHandle, ppvBuffers, lengths); } else { FileSystem.ReadFromStreamScatter(fileHandle, ppvBuffers, lengths); } }
public unsafe static void FillStencilFromPoint(Surface surface, IBitVector2D stencil, Point start, int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection) { ColorBgra cmp = surface[start]; int top = int.MaxValue; int bottom = int.MinValue; int left = int.MaxValue; int right = int.MinValue; Rectangle[] scans; stencil.Clear(false); if (limitToSelection) { using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height))) { excluded.Xor(limitRegion); scans = excluded.GetRegionScansReadOnlyInt(); } } else { scans = new Rectangle[0]; } foreach (Rectangle rect in scans) { stencil.Set(rect, true); } Queue <Point> queue = new Queue <Point>(16); queue.Enqueue(start); while (queue.Count > 0) { Point pt = queue.Dequeue(); ColorBgra *rowPtr = surface.GetRowAddressUnchecked(pt.Y); int localLeft = pt.X - 1; int localRight = pt.X; while (localLeft >= 0 && !stencil.GetUnchecked(localLeft, pt.Y) && CheckColor(cmp, rowPtr[localLeft], tolerance)) { stencil.SetUnchecked(localLeft, pt.Y, true); --localLeft; } while (localRight < surface.Width && !stencil.GetUnchecked(localRight, pt.Y) && CheckColor(cmp, rowPtr[localRight], tolerance)) { stencil.SetUnchecked(localRight, pt.Y, true); ++localRight; } ++localLeft; --localRight; if (pt.Y > 0) { int sleft = localLeft; int sright = localLeft; ColorBgra *rowPtrUp = surface.GetRowAddressUnchecked(pt.Y - 1); for (int sx = localLeft; sx <= localRight; ++sx) { if (!stencil.GetUnchecked(sx, pt.Y - 1) && CheckColor(cmp, rowPtrUp[sx], tolerance)) { ++sright; } else { if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y - 1)); } ++sright; sleft = sright; } } if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y - 1)); } } if (pt.Y < surface.Height - 1) { int sleft = localLeft; int sright = localLeft; ColorBgra *rowPtrDown = surface.GetRowAddressUnchecked(pt.Y + 1); for (int sx = localLeft; sx <= localRight; ++sx) { if (!stencil.GetUnchecked(sx, pt.Y + 1) && CheckColor(cmp, rowPtrDown[sx], tolerance)) { ++sright; } else { if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y + 1)); } ++sright; sleft = sright; } } if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y + 1)); } } if (localLeft < left) { left = localLeft; } if (localRight > right) { right = localRight; } if (pt.Y < top) { top = pt.Y; } if (pt.Y > bottom) { bottom = pt.Y; } } foreach (Rectangle rect in scans) { stencil.Set(rect, false); } boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1); }
public static unsafe void FillStencilFromPoint(Surface surface, IBitVector2D stencil, Point start, int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection) { ColorBgra cmp = surface[start]; int top = int.MaxValue; int bottom = int.MinValue; int left = int.MaxValue; int right = int.MinValue; Rectangle[] scans; stencil.Clear(false); if (limitToSelection) { using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height))) { excluded.Xor(limitRegion); scans = excluded.GetRegionScansReadOnlyInt(); } } else { scans = new Rectangle[0]; } foreach (Rectangle rect in scans) { stencil.Set(rect, true); } Queue<Point> queue = new Queue<Point>(16); queue.Enqueue(start); while (queue.Count > 0) { Point pt = queue.Dequeue(); ColorBgra* rowPtr = surface.GetRowAddressUnchecked(pt.Y); int localLeft = pt.X - 1; int localRight = pt.X; while (localLeft >= 0 && !stencil.GetUnchecked(localLeft, pt.Y) && CheckColor(cmp, rowPtr[localLeft], tolerance)) { stencil.SetUnchecked(localLeft, pt.Y, true); --localLeft; } while (localRight < surface.Width && !stencil.GetUnchecked(localRight, pt.Y) && CheckColor(cmp, rowPtr[localRight], tolerance)) { stencil.SetUnchecked(localRight, pt.Y, true); ++localRight; } ++localLeft; --localRight; if (pt.Y > 0) { int sleft = localLeft; int sright = localLeft; ColorBgra* rowPtrUp = surface.GetRowAddressUnchecked(pt.Y - 1); for (int sx = localLeft; sx <= localRight; ++sx) { if (!stencil.GetUnchecked(sx, pt.Y - 1) && CheckColor(cmp, rowPtrUp[sx], tolerance)) { ++sright; } else { if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y - 1)); } ++sright; sleft = sright; } } if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y - 1)); } } if (pt.Y < surface.Height - 1) { int sleft = localLeft; int sright = localLeft; ColorBgra* rowPtrDown = surface.GetRowAddressUnchecked(pt.Y + 1); for (int sx = localLeft; sx <= localRight; ++sx) { if (!stencil.GetUnchecked(sx, pt.Y + 1) && CheckColor(cmp, rowPtrDown[sx], tolerance)) { ++sright; } else { if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y + 1)); } ++sright; sleft = sright; } } if (sright - sleft > 0) { queue.Enqueue(new Point(sleft, pt.Y + 1)); } } if (localLeft < left) { left = localLeft; } if (localRight > right) { right = localRight; } if (pt.Y < top) { top = pt.Y; } if (pt.Y > bottom) { bottom = pt.Y; } } foreach (Rectangle rect in scans) { stencil.Set(rect, false); } boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1); }
private Rectangle[][] SliceUpRegion(PdnRegion region, int sliceCount, Rectangle layerBounds) { Rectangle[][] slices = new Rectangle[sliceCount][]; Rectangle[] regionRects = region.GetRegionScansReadOnlyInt(); Scanline[] regionScans = Utility.GetRegionScans(regionRects); for (int i = 0; i < sliceCount; ++i) { int beginScan = (regionScans.Length * i) / sliceCount; int endScan = Math.Min(regionScans.Length, (regionScans.Length * (i + 1)) / sliceCount); // Try to arrange it such that the maximum size of the first region // is 1-pixel tall if (i == 0) { endScan = Math.Min(endScan, beginScan + 1); } else if (i == 1) { beginScan = Math.Min(beginScan, 1); } Rectangle[] newRects = Utility.ScanlinesToRectangles(regionScans, beginScan, endScan - beginScan); for (int j = 0; j < newRects.Length; ++j) { newRects[j].Intersect(layerBounds); } slices[i] = newRects; } return slices; }
public void Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, PdnRegion roi) { Rectangle[] scans = roi.GetRegionScansReadOnlyInt(); Render(parameters, dstArgs, srcArgs, scans, 0, scans.Length); }
public unsafe static void FillStencilByColor(Surface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection) { int top = int.MaxValue; int bottom = int.MinValue; int left = int.MaxValue; int right = int.MinValue; Rectangle[] scans; stencil.Clear(false); if (limitToSelection) { using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height))) { excluded.Xor(limitRegion); scans = excluded.GetRegionScansReadOnlyInt(); } } else { scans = new Rectangle[0]; } foreach (Rectangle rect in scans) { stencil.Set(rect, true); } for (int y = 0; y < surface.Height; ++y) { bool foundPixelInRow = false; ColorBgra *ptr = surface.GetRowAddressUnchecked(y); for (int x = 0; x < surface.Width; ++x) { if (CheckColor(cmp, *ptr, tolerance)) { stencil.SetUnchecked(x, y, true); if (x < left) { left = x; } if (x > right) { right = x; } foundPixelInRow = true; } ++ptr; } if (foundPixelInRow) { if (y < top) { top = y; } if (y >= bottom) { bottom = y; } } } foreach (Rectangle rect in scans) { stencil.Set(rect, false); } boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1); }
public override HistoryMemento OnExecute(IHistoryWorkspace historyWorkspace) { if (historyWorkspace.Selection.IsEmpty) { return(null); } else { PdnRegion selectionRegion = historyWorkspace.Selection.CreateRegion(); if (selectionRegion.GetArea() == 0) { selectionRegion.Dispose(); return(null); } SelectionHistoryMemento sha = new SelectionHistoryMemento(StaticName, null, historyWorkspace); ReplaceDocumentHistoryMemento rdha = new ReplaceDocumentHistoryMemento(StaticName, null, historyWorkspace); Rectangle boundingBox; Rectangle[] inverseRegionRects = null; boundingBox = Utility.GetRegionBounds(selectionRegion); using (PdnRegion inverseRegion = new PdnRegion(boundingBox)) { inverseRegion.Exclude(selectionRegion); inverseRegionRects = Utility.TranslateRectangles( inverseRegion.GetRegionScansReadOnlyInt(), -boundingBox.X, -boundingBox.Y); } selectionRegion.Dispose(); selectionRegion = null; Document oldDocument = historyWorkspace.Document; // TODO: serialize this to disk so we don't *have* to store the full thing Document newDocument = new Document(boundingBox.Width, boundingBox.Height); // copy the document's meta data over newDocument.ReplaceMetaDataFrom(oldDocument); foreach (Layer layer in oldDocument.Layers) { if (layer is BitmapLayer) { BitmapLayer oldLayer = (BitmapLayer)layer; Surface croppedSurface = oldLayer.Surface.CreateWindow(boundingBox); BitmapLayer newLayer = new BitmapLayer(croppedSurface); ColorBgra clearWhite = ColorBgra.White.NewAlpha(0); foreach (Rectangle rect in inverseRegionRects) { newLayer.Surface.Clear(rect, clearWhite); } newLayer.LoadProperties(oldLayer.SaveProperties()); newDocument.Layers.Add(newLayer); } else { throw new InvalidOperationException("Crop does not support Layers that are not BitmapLayers"); } } CompoundHistoryMemento cha = new CompoundHistoryMemento( StaticName, PdnResources.GetImageResource("Icons.MenuImageCropIcon.png"), new HistoryMemento[] { sha, rdha }); EnterCriticalRegion(); historyWorkspace.Document = newDocument; return(cha); } }
public static unsafe void FillStencilByColor(Surface surface, IBitVector2D stencil, ColorBgra cmp, int tolerance, out Rectangle boundingBox, PdnRegion limitRegion, bool limitToSelection) { int top = int.MaxValue; int bottom = int.MinValue; int left = int.MaxValue; int right = int.MinValue; Rectangle[] scans; stencil.Clear(false); if (limitToSelection) { using (PdnRegion excluded = new PdnRegion(new Rectangle(0, 0, stencil.Width, stencil.Height))) { excluded.Xor(limitRegion); scans = excluded.GetRegionScansReadOnlyInt(); } } else { scans = new Rectangle[0]; } foreach (Rectangle rect in scans) { stencil.Set(rect, true); } for (int y = 0; y < surface.Height; ++y) { bool foundPixelInRow = false; ColorBgra *ptr = surface.GetRowAddressUnchecked(y); for (int x = 0; x < surface.Width; ++x) { if (CheckColor(cmp, *ptr, tolerance)) { stencil.SetUnchecked(x, y, true); if (x < left) { left = x; } if (x > right) { right = x; } foundPixelInRow = true; } ++ptr; } if (foundPixelInRow) { if (y < top) { top = y; } if (y >= bottom) { bottom = y; } } } foreach (Rectangle rect in scans) { stencil.Set(rect, false); } boundingBox = Rectangle.FromLTRB(left, top, right + 1, bottom + 1); }