예제 #1
0
        /// <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>
        private void CopySurface(MaskSurface source)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("Surface");
            }

            if (stride == source.stride &&
                width == source.width &&
                height == source.height)
            {
                unsafe
                {
                    Memory.Copy(scan0.VoidStar,
                                source.scan0.VoidStar,
                                ((ulong)(height - 1) * (ulong)stride) + (ulong)width);
                }
            }
            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);
                    }
                }
            }
        }
예제 #2
0
        public MaskSurface Clone()
        {
            MaskSurface surface = new MaskSurface(width, height);

            surface.CopySurface(this);
            return(surface);
        }
예제 #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelPortsSuite"/> class.
        /// </summary>
        /// <param name="filterImageProvider">The filter image provider.</param>
        /// <exception cref="ArgumentNullException"><paramref name="filterImageProvider"/> is null.</exception>
        public unsafe ChannelPortsSuite(IFilterImageProvider filterImageProvider)
        {
            if (filterImageProvider == null)
            {
                throw new ArgumentNullException(nameof(filterImageProvider));
            }

            this.filterImageProvider = filterImageProvider;
            readPixelsProc           = new ReadPixelsProc(ReadPixelsProc);
            writeBasePixelsProc      = new WriteBasePixelsProc(WriteBasePixels);
            readPortForWritePortProc = new ReadPortForWritePortProc(ReadPortForWritePort);
            scaledChannelSurface     = null;
            scaledSelectionMask      = null;
            disposed = false;
        }
예제 #4
0
        public void Dispose()
        {
            if (!disposed)
            {
                disposed = true;

                if (scaledChannelSurface != null)
                {
                    scaledChannelSurface.Dispose();
                    scaledChannelSurface = null;
                }

                if (scaledSelectionMask != null)
                {
                    scaledSelectionMask.Dispose();
                    scaledSelectionMask = null;
                }
            }
        }
예제 #5
0
        private static unsafe void FillSelectionMask(PixelMemoryDesc *destiniation, MaskSurface source, VRect srcRect)
        {
            byte *dstPtr = (byte *)destiniation->data.ToPointer();
            int   stride = destiniation->rowBits / 8;
            int   bpp    = destiniation->colBits / 8;
            int   offset = destiniation->bitOffset / 8;

            for (int y = srcRect.top; y < srcRect.bottom; y++)
            {
                byte *src = source.GetPointAddressUnchecked(srcRect.left, y);
                byte *dst = dstPtr + (y * stride) + offset;
                for (int x = srcRect.left; x < srcRect.right; x++)
                {
                    *dst = *src;

                    src++;
                    dst += bpp;
                }
            }
        }
예제 #6
0
        /// <summary>
        /// Fits the source surface to this surface using bicubic interpolation.
        /// </summary>
        /// <param name="source">The Surface to read pixels from.</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(MaskSurface source)
        {
            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)
            };
            Rectangle dstRoi = Bounds;

            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]);
                    }
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Implements bicubic filtering with NO bounds checking at any pixel.
        /// </summary>
        private unsafe void BicubicFitSurfaceUnchecked(MaskSurface source, Rectangle dstRoi)
        {
            Rectangle roi   = Rectangle.Intersect(dstRoi, Bounds);
            Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));

            IntPtr  rColCacheIP = Memory.Allocate(4 * (long)roi.Width * (long)sizeof(double), false);
            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;

                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;
                byte * dstPtr      = 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();
                byte *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 graySum     = 0;
                    double alphaSum    = 0;
                    double totalWeight = 0;

                    byte *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 = 255.0;
                        double a1 = 255.0;
                        double a2 = 255.0;
                        double a3 = 255.0;

                        alphaSum    += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3);
                        totalWeight += w0 + w1 + w2 + w3;

                        graySum += (a0 * srcPtr[0] * w0) + (a1 * srcPtr[1] * w1) + (a2 * srcPtr[2] * w2) + (a3 * srcPtr[3] * w3);

                        srcPtr = ((byte *)srcPtr + source.stride);
                    }

                    double alpha = alphaSum / totalWeight;

                    double gray;

                    if (alpha == 0)
                    {
                        gray = 0;
                    }
                    else
                    {
                        gray = graySum / alphaSum;
                        // add 0.5 to ensure truncation to uint results in rounding
                        gray += 0.5;
                    }

                    *dstPtr = (byte)gray;
                    ++dstPtr;
                    rColCache += 4;
                } // for (dstX...
            }     // for (dstY...

            Memory.Free(rColCacheIP);
        }
예제 #8
0
        /// <summary>
        /// Implements bicubic filtering with bounds checking at every pixel.
        /// </summary>
        private unsafe void BicubicFitSurfaceChecked(MaskSurface source, Rectangle dstRoi)
        {
            Rectangle roi   = Rectangle.Intersect(dstRoi, Bounds);
            Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));

            IntPtr  rColCacheIP = Memory.Allocate(4 * (long)roi.Width * (long)sizeof(double), false);
            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;
                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;
                byte * dstPtr      = 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 graySum     = 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);

                    byte *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;

                                graySum  += *srcPtr * w * 255.0;
                                alphaSum += 255.0 * w;

                                totalWeight += w;
                            }

                            ++srcPtr;
                        }

                        srcPtr = ((byte *)(srcPtr - 4) + source.stride);
                    }

                    double alpha = alphaSum / totalWeight;
                    double gray;

                    if (alpha == 0)
                    {
                        gray = 0;
                    }
                    else
                    {
                        gray = graySum / alphaSum;

                        // add 0.5 to ensure truncation to uint results in rounding
                        gray += 0.5;
                    }

                    *dstPtr = (byte)gray;
                    ++dstPtr;
                } // for (dstX...
            }     // for (dstY...

            Memory.Free(rColCacheIP);
        }
예제 #9
0
        public unsafe void SuperSampleFitSurface(MaskSurface source)
        {
            Rectangle dstRoi2   = Rectangle.Intersect(source.Bounds, Bounds);
            int       srcHeight = source.Height;
            int       srcWidth  = source.Width;
            long      srcStride = source.Stride;

            for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY)
            {
                double srcTop       = (double)(dstY * srcHeight) / (double)height;
                double srcTopFloor  = Math.Floor(srcTop);
                double srcTopWeight = 1 - (srcTop - srcTopFloor);
                int    srcTopInt    = (int)srcTopFloor;

                double srcBottom       = (double)((dstY + 1) * srcHeight) / (double)height;
                double srcBottomFloor  = Math.Floor(srcBottom - 0.00001);
                double srcBottomWeight = srcBottom - srcBottomFloor;
                int    srcBottomInt    = (int)srcBottomFloor;

                byte *dstPtr = GetPointAddressUnchecked(dstRoi2.Left, dstY);

                for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX)
                {
                    double srcLeft       = (double)(dstX * srcWidth) / (double)width;
                    double srcLeftFloor  = Math.Floor(srcLeft);
                    double srcLeftWeight = 1 - (srcLeft - srcLeftFloor);
                    int    srcLeftInt    = (int)srcLeftFloor;

                    double srcRight       = (double)((dstX + 1) * srcWidth) / (double)width;
                    double srcRightFloor  = Math.Floor(srcRight - 0.00001);
                    double srcRightWeight = srcRight - srcRightFloor;
                    int    srcRightInt    = (int)srcRightFloor;

                    double graySum  = 0;
                    double alphaSum = 0;

                    // left fractional edge
                    byte *srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1);

                    for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
                    {
                        graySum   += *srcLeftPtr * srcLeftWeight * 255.0;
                        srcLeftPtr = (byte *)((byte *)srcLeftPtr + srcStride);
                    }

                    // right fractional edge
                    byte *srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1);
                    for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
                    {
                        graySum    += *srcLeftPtr * srcLeftWeight * 255.0;
                        srcRightPtr = (byte *)((byte *)srcRightPtr + srcStride);
                    }

                    // top fractional edge
                    byte *srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt);
                    for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
                    {
                        graySum += *srcLeftPtr * srcLeftWeight * 255.0;

                        ++srcTopPtr;
                    }

                    // bottom fractional edge
                    byte *srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt);
                    for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
                    {
                        graySum += *srcLeftPtr * srcLeftWeight * 255.0;

                        ++srcBottomPtr;
                    }

                    // center area
                    for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
                    {
                        byte *srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY);

                        for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
                        {
                            graySum += *srcLeftPtr * srcLeftWeight * 255.0;

                            ++srcPtr;
                        }
                    }

                    // four corner pixels
                    byte srcTL = source.GetPoint(srcLeftInt, srcTopInt);
                    graySum  += srcTL * (srcTopWeight * srcLeftWeight) * 255.0;
                    alphaSum += 255.0 * (srcTopWeight * srcLeftWeight);

                    byte srcTR = source.GetPoint(srcRightInt, srcTopInt);
                    graySum  += srcTR * (srcTopWeight * srcRightWeight) * 255.0;
                    alphaSum += 255.0 * (srcTopWeight * srcRightWeight);

                    byte srcBL = source.GetPoint(srcLeftInt, srcBottomInt);
                    graySum  += srcBL * (srcBottomWeight * srcLeftWeight) * 255.0;
                    alphaSum += 255.0 * (srcBottomWeight * srcLeftWeight);

                    byte srcBR = source.GetPoint(srcRightInt, srcBottomInt);
                    graySum  += srcBR * (srcBottomWeight * srcRightWeight) * 255.0;
                    alphaSum += 255.0 * (srcBottomWeight * srcRightWeight);

                    double area = (srcRight - srcLeft) * (srcBottom - srcTop);

                    double alpha = 255.0 / area;
                    double gray;

                    if (alpha == 0)
                    {
                        gray = 0;
                    }
                    else
                    {
                        gray = graySum / alphaSum;
                    }

                    // add 0.5 so that rounding goes in the direction we want it to
                    gray += 0.5;

                    *dstPtr = (byte)gray;
                    ++dstPtr;
                }
            }
        }
예제 #10
0
        private unsafe short ReadPixelsProc(IntPtr port, PSScaling *scaling, VRect *writeRect, PixelMemoryDesc *destination, VRect *wroteRect)
        {
#if DEBUG
            DebugUtils.Ping(DebugFlags.ChannelPorts, string.Format("port: {0}, rect: {1}", port.ToString(), DebugUtils.PointerToString(writeRect)));
#endif
            if (scaling == null || writeRect == null || destination == null)
            {
                return(PSError.paramErr);
            }

            if (destination->depth != 8)
            {
                return(PSError.errUnsupportedDepth);
            }

            if ((destination->bitOffset % 8) != 0)
            {
                return(PSError.errUnsupportedBitOffset);
            }

            if ((destination->colBits % 8) != 0)
            {
                return(PSError.errUnsupportedColBits);
            }

            if ((destination->rowBits % 8) != 0)
            {
                return(PSError.errUnsupportedRowBits);
            }

            int channel = port.ToInt32();

            if (channel < PSConstants.ChannelPorts.Red || channel > PSConstants.ChannelPorts.SelectionMask)
            {
                return(PSError.errUnknownPort);
            }

            VRect srcRect = scaling->sourceRect;
            VRect dstRect = scaling->destinationRect;

            int srcWidth  = srcRect.right - srcRect.left;
            int srcHeight = srcRect.bottom - srcRect.top;
            int dstWidth  = dstRect.right - dstRect.left;
            int dstHeight = dstRect.bottom - dstRect.top;

            if (channel == PSConstants.ChannelPorts.SelectionMask)
            {
                if (srcWidth == dstWidth && srcHeight == dstHeight)
                {
                    FillSelectionMask(destination, filterImageProvider.Mask, srcRect);
                }
                else if (dstWidth < srcWidth || dstHeight < srcHeight) // scale down
                {
                    if ((scaledSelectionMask == null) || scaledSelectionMask.Width != dstWidth || scaledSelectionMask.Height != dstHeight)
                    {
                        if (scaledSelectionMask != null)
                        {
                            scaledSelectionMask.Dispose();
                            scaledSelectionMask = null;
                        }

                        scaledSelectionMask = new MaskSurface(dstWidth, dstHeight);
                        scaledSelectionMask.SuperSampleFitSurface(filterImageProvider.Mask);
                    }

                    FillSelectionMask(destination, scaledSelectionMask, dstRect);
                }
                else if (dstWidth > srcWidth || dstHeight > srcHeight) // scale up
                {
                    if ((scaledSelectionMask == null) || scaledSelectionMask.Width != dstWidth || scaledSelectionMask.Height != dstHeight)
                    {
                        if (scaledSelectionMask != null)
                        {
                            scaledSelectionMask.Dispose();
                            scaledSelectionMask = null;
                        }

                        scaledSelectionMask = new MaskSurface(dstWidth, dstHeight);
                        scaledSelectionMask.BicubicFitSurface(filterImageProvider.Mask);
                    }

                    FillSelectionMask(destination, scaledSelectionMask, dstRect);
                }
            }
            else
            {
                if (srcWidth == dstWidth && srcHeight == dstHeight)
                {
                    FillChannelData(channel, destination, filterImageProvider.Source, srcRect);
                }
                else if (dstWidth < srcWidth || dstHeight < srcHeight) // scale down
                {
                    if ((scaledChannelSurface == null) || scaledChannelSurface.Width != dstWidth || scaledChannelSurface.Height != dstHeight)
                    {
                        if (scaledChannelSurface != null)
                        {
                            scaledChannelSurface.Dispose();
                            scaledChannelSurface = null;
                        }

                        scaledChannelSurface = new Surface(dstWidth, dstHeight);
                        scaledChannelSurface.SuperSampleFitSurface(filterImageProvider.Source);
                    }

                    FillChannelData(channel, destination, scaledChannelSurface, dstRect);
                }
                else if (dstWidth > srcWidth || dstHeight > srcHeight) // scale up
                {
                    if ((scaledChannelSurface == null) || scaledChannelSurface.Width != dstWidth || scaledChannelSurface.Height != dstHeight)
                    {
                        if (scaledChannelSurface != null)
                        {
                            scaledChannelSurface.Dispose();
                            scaledChannelSurface = null;
                        }

                        scaledChannelSurface = new Surface(dstWidth, dstHeight);
                        scaledChannelSurface.BicubicFitSurface(filterImageProvider.Source);
                    }

                    FillChannelData(channel, destination, scaledChannelSurface, dstRect);
                }
            }

            if (wroteRect != null)
            {
                *wroteRect = dstRect;
            }

            return(PSError.noErr);
        }