Example #1
0
        /// <summary>
        /// Computes the weights to apply at each pixel when resizing.
        /// </summary>
        /// <param name="sampler">The <see cref="IResampler"/></param>
        /// <param name="destinationSize">The destination size</param>
        /// <param name="sourceSize">The source size</param>
        /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations</param>
        /// <returns>The <see cref="KernelMap"/></returns>
        public static KernelMap Calculate(
            IResampler sampler,
            int destinationSize,
            int sourceSize,
            MemoryAllocator memoryAllocator)
        {
            float ratio = (float)sourceSize / destinationSize;
            float scale = ratio;

            if (scale < 1F)
            {
                scale = 1F;
            }

            float radius = MathF.Ceiling(scale * sampler.Radius);
            var   result = new KernelMap(memoryAllocator, destinationSize, radius);

            for (int i = 0; i < destinationSize; i++)
            {
                float center = ((i + .5F) * ratio) - .5F;

                // Keep inside bounds.
                int left = (int)MathF.Ceiling(center - radius);
                if (left < 0)
                {
                    left = 0;
                }

                int right = (int)MathF.Floor(center + radius);
                if (right > sourceSize - 1)
                {
                    right = sourceSize - 1;
                }

                float sum = 0;

                ResizeKernel ws = result.CreateKernel(i, left, right);
                result.Kernels[i] = ws;

                ref float weightsBaseRef = ref ws.GetStartReference();

                for (int j = left; j <= right; j++)
                {
                    float weight = sampler.GetValue((j - center) / scale);
                    sum += weight;

                    // weights[j - left] = weight:
                    Unsafe.Add(ref weightsBaseRef, j - left) = weight;
                }

                // Normalize, best to do it here rather than in the pixel loop later on.
                if (sum > 0)
                {
                    for (int w = 0; w < ws.Length; w++)
                    {
                        // weights[w] = weights[w] / sum:
                        ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w);
                        wRef /= sum;
                    }
                }
Example #2
0
            protected override void Initialize()
            {
                // Build top corner data + one period of the mosaic data:
                int startOfFirstRepeatedMosaic = this.cornerInterval + this.period;

                for (int i = 0; i < startOfFirstRepeatedMosaic; i++)
                {
                    ResizeKernel kernel = this.BuildKernel(i, i);
                    this.kernels[i] = kernel;
                }

                // Copy the mosaics:
                int bottomStartDest = this.DestinationLength - this.cornerInterval;

                for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++)
                {
                    double       center = ((i + .5) * this.ratio) - .5;
                    int          left   = (int)TolerantMath.Ceiling(center - this.radius);
                    ResizeKernel kernel = this.kernels[i - this.period];
                    this.kernels[i] = kernel.AlterLeftValue(left);
                }

                // Build bottom corner data:
                int bottomStartData = this.cornerInterval + this.period;

                for (int i = 0; i < this.cornerInterval; i++)
                {
                    ResizeKernel kernel = this.BuildKernel(bottomStartDest + i, bottomStartData + i);
                    this.kernels[bottomStartDest + i] = kernel;
                }
            }
 protected virtual void Initialize()
 {
     for (int i = 0; i < this.DestinationLength; i++)
     {
         ResizeKernel kernel = this.BuildKernel(i, i);
         this.kernels[i] = kernel;
     }
 }
Example #4
0
        public void FillDestinationPixels(RowInterval rowInterval, Buffer2D <TPixel> destination)
        {
            Span <Vector4> tempColSpan = this.tempColumnBuffer.GetSpan();

            for (int y = rowInterval.Min; y < rowInterval.Max; y++)
            {
                // Ensure offsets are normalized for cropping and padding.
                ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - this.targetOrigin.Y);

                if (kernel.StartIndex + kernel.Length > this.currentWindow.Max)
                {
                    this.Slide();
                }

                ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan);

                int top = kernel.StartIndex - this.currentWindow.Min;

                ref Vector4 fpBase = ref this.transposedFirstPassBuffer.Span[top];
        /// <summary>
        /// Builds a <see cref="ResizeKernel"/> for the row <paramref name="destRowIndex"/> (in <see cref="kernels"/>)
        /// referencing the data at row <paramref name="dataRowIndex"/> within <see cref="data"/>,
        /// so the data reusable by other data rows.
        /// </summary>
        private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex)
        {
            double center = ((destRowIndex + .5) * this.ratio) - .5;

            // Keep inside bounds.
            int left = (int)TolerantMath.Ceiling(center - this.radius);

            if (left < 0)
            {
                left = 0;
            }

            int right = (int)TolerantMath.Floor(center + this.radius);

            if (right > this.sourceLength - 1)
            {
                right = this.sourceLength - 1;
            }

            ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right);

            Span <double> kernelValues = this.tempValues.AsSpan().Slice(0, kernel.Length);
            double        sum          = 0;

            for (int j = left; j <= right; j++)
            {
                double value = this.sampler.GetValue((float)((j - center) / this.scale));
                sum += value;

                kernelValues[j - left] = value;
            }

            // Normalize, best to do it here rather than in the pixel loop later on.
            if (sum > 0)
            {
                for (int j = 0; j < kernel.Length; j++)
                {
                    // weights[w] = weights[w] / sum:
                    ref double kRef = ref kernelValues[j];
                    kRef /= sum;
                }
            }
Example #6
0
        /// <inheritdoc/>
        protected override void OnFrameApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
        {
            // Handle resize dimensions identical to the original
            if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.ResizeRectangle)
            {
                // The cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
                return;
            }

            int width   = this.Width;
            int height  = this.Height;
            int sourceX = sourceRectangle.X;
            int sourceY = sourceRectangle.Y;
            int startY  = this.ResizeRectangle.Y;
            int endY    = this.ResizeRectangle.Bottom;
            int startX  = this.ResizeRectangle.X;
            int endX    = this.ResizeRectangle.Right;

            int minX = Math.Max(0, startX);
            int maxX = Math.Min(width, endX);
            int minY = Math.Max(0, startY);
            int maxY = Math.Min(height, endY);

            if (this.Sampler is NearestNeighborResampler)
            {
                var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY);

                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;

                ParallelHelper.IterateRows(
                    workingRect,
                    configuration,
                    rows =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Y coordinates of source points
                        Span <TPixel> sourceRow =
                            source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
                        Span <TPixel> targetRow = destination.GetPixelRowSpan(y);

                        for (int x = minX; x < maxX; x++)
                        {
                            // X coordinates of source points
                            targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)];
                        }
                    }
                });

                return;
            }

            int sourceHeight = source.Height;

            PixelConversionModifiers conversionModifiers = PixelConversionModifiers.Premultiply;

            if (this.Compand)
            {
                conversionModifiers |= PixelConversionModifiers.Scale | PixelConversionModifiers.SRgbCompand;
            }

            // Interpolate the image using the calculated weights.
            // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
            // First process the columns. Since we are not using multiple threads startY and endY
            // are the upper and lower bounds of the source rectangle.
            using (Buffer2D <Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D <Vector4>(sourceHeight, width))
            {
                firstPassPixelsTransposed.MemorySource.Clear();

                var processColsRect = new Rectangle(0, 0, source.Width, sourceRectangle.Bottom);

                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processColsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        Span <TPixel> sourceRow    = source.GetPixelRowSpan(y).Slice(sourceX);
                        Span <Vector4> tempRowSpan = tempRowBuffer.Span.Slice(sourceX);

                        PixelOperations <TPixel> .Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers);

                        ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];

                        for (int x = minX; x < maxX; x++)
                        {
                            ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
                            Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan);
                        }
                    }
                });

                var processRowsRect = Rectangle.FromLTRB(0, minY, width, maxY);

                // Now process the rows.
                ParallelHelper.IterateRowsWithTempBuffer <Vector4>(
                    processRowsRect,
                    configuration,
                    (rows, tempRowBuffer) =>
                {
                    Span <Vector4> tempRowSpan = tempRowBuffer.Span;

                    for (int y = rows.Min; y < rows.Max; y++)
                    {
                        // Ensure offsets are normalized for cropping and padding.
                        ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY);

                        ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempRowSpan);

                        for (int x = 0; x < width; x++)
                        {
                            Span <Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);

                            // Destination color components
                            Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
                        }

                        Span <TPixel> targetRowSpan = destination.GetPixelRowSpan(y);

                        PixelOperations <TPixel> .Instance.FromVector4Destructive(configuration, tempRowSpan, targetRowSpan, conversionModifiers);
                    }
                });
            }
        }