///////////////////////////////////////////////////////////////////////////////// // Paint.NET // // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. // // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. // // See license-pdn.txt for full licensing and attribution details. // // // // Ported to Pinta by: Jonathan Pobst // ///////////////////////////////////////////////////////////////////////////////// using System; using Gdk; namespace Pinta.Core { /// /// Defines a way to operate on a pixel, or a region of pixels, in a binary fashion. /// That is, it is a simple function F that takes two parameters and returns a /// result of the form: c = F(a, b) /// [Serializable] public unsafe abstract class BinaryPixelOp : PixelOp { public abstract ColorBgra Apply (ColorBgra lhs, ColorBgra rhs); public unsafe virtual void Apply (ColorBgra* dst, ColorBgra* lhs, ColorBgra* rhs, int length) { unsafe { while (length > 0) { *dst = Apply (*lhs, *rhs); ++dst; ++lhs; ++rhs; --length; } } } /// /// Provides a default implementation for performing dst = F(lhs, rhs) over some rectangle of interest. /// /// The Surface to write pixels to. /// The pixel offset that defines the upper-left of the rectangle-of-interest for the dst Surface. /// The Surface to read pixels from for the lhs parameter given to the method ColorBgra Apply(ColorBgra, ColorBgra)b>. /// The pixel offset that defines the upper-left of the rectangle-of-interest for the lhs Surface. /// The Surface to read pixels from for the rhs parameter given to the method ColorBgra Apply(ColorBgra, ColorBgra) /// The pixel offset that defines the upper-left of the rectangle-of-interest for the rhs Surface. /// The size of the rectangles-of-interest for all Surfaces. public void Apply (Cairo.ImageSurface dst, Point dstOffset, Cairo.ImageSurface lhs, Point lhsOffset, Cairo.ImageSurface rhs, Point rhsOffset, Size roiSize) { // Bounds checking only enabled in Debug builds. #if DEBUG // Create bounding rectangles for each Surface Rectangle dstRect = new Rectangle (dstOffset, roiSize); Rectangle lhsRect = new Rectangle (lhsOffset, roiSize); Rectangle rhsRect = new Rectangle (rhsOffset, roiSize); // Clip those rectangles to those Surface's bounding rectangles Rectangle dstClip = Rectangle.Intersect (dstRect, dst.GetBounds ()); Rectangle lhsClip = Rectangle.Intersect (lhsRect, lhs.GetBounds ()); Rectangle rhsClip = Rectangle.Intersect (rhsRect, rhs.GetBounds ()); // If any of those Rectangles actually got clipped, then throw an exception if (dstRect != dstClip) { throw new ArgumentOutOfRangeException ("roiSize", "Destination roi out of bounds"); } if (lhsRect != lhsClip) { throw new ArgumentOutOfRangeException ("roiSize", "lhs roi out of bounds"); } if (rhsRect != rhsClip) { throw new ArgumentOutOfRangeException ("roiSize", "rhs roi out of bounds"); } #endif // Cache the width and height properties int width = roiSize.Width; int height = roiSize.Height; // Do the work. unsafe { for (int row = 0; row < height; ++row) { ColorBgra* dstPtr = dst.GetPointAddress (dstOffset.X, dstOffset.Y + row); ColorBgra* lhsPtr = lhs.GetPointAddress (lhsOffset.X, lhsOffset.Y + row); ColorBgra* rhsPtr = rhs.GetPointAddress (rhsOffset.X, rhsOffset.Y + row); Apply (dstPtr, lhsPtr, rhsPtr, width); } } } public unsafe override void Apply (ColorBgra* dst, ColorBgra* src, int length) { unsafe { while (length > 0) { *dst = Apply (*dst, *src); ++dst; ++src; --length; } } } public override void Apply (Cairo.ImageSurface dst, Point dstOffset, Cairo.ImageSurface src, Point srcOffset, int roiLength) { Apply (dst.GetPointAddress (dstOffset), src.GetPointAddress (srcOffset), roiLength); } public void Apply (Cairo.ImageSurface dst, Cairo.ImageSurface src) { if (dst.GetSize () != src.GetSize ()) { throw new ArgumentException ("dst.Size != src.Size"); } unsafe { for (int y = 0; y < dst.Height; ++y) { ColorBgra* dstPtr = dst.GetRowAddressUnchecked (y); ColorBgra* srcPtr = src.GetRowAddressUnchecked (y); Apply (dstPtr, srcPtr, dst.Width); } } } public void Apply (Cairo.ImageSurface dst, Cairo.ImageSurface lhs, Cairo.ImageSurface rhs) { if (dst.GetSize () != lhs.GetSize ()) { throw new ArgumentException ("dst.Size != lhs.Size"); } if (lhs.GetSize () != rhs.GetSize ()) { throw new ArgumentException ("lhs.Size != rhs.Size"); } unsafe { for (int y = 0; y < dst.Height; ++y) { ColorBgra* dstPtr = dst.GetRowAddressUnchecked (y); ColorBgra* lhsPtr = lhs.GetRowAddressUnchecked (y); ColorBgra* rhsPtr = rhs.GetRowAddressUnchecked (y); Apply (dstPtr, lhsPtr, rhsPtr, dst.Width); } } } protected BinaryPixelOp () { } } }