예제 #1
0
        /// <summary>
        /// Computes the weights to apply at each pixel when resizing.
        /// </summary>
        /// <param name="destinationSize">The destination size</param>
        /// <param name="sourceSize">The source size</param>
        /// <returns>The <see cref="WeightsBuffer"/></returns>
        // TODO: Made internal to simplify experimenting with weights data. Make it protected again when finished figuring out how to optimize all the stuff!
        internal unsafe WeightsBuffer PrecomputeWeights(int destinationSize, int sourceSize)
        {
            float ratio = (float)sourceSize / destinationSize;
            float scale = ratio;

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

            IResampler sampler = this.Sampler;
            float      radius  = MathF.Ceiling(scale * sampler.Radius);
            var        result  = new WeightsBuffer(sourceSize, destinationSize);

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

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

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

                float sum = 0;

                WeightsWindow ws = result.GetWeightsWindow(i, left, right);
                result.Weights[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;
                }

                // Normalise, 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 = wRef / sum;
                    }
                }
예제 #2
0
        /// <inheritdoc/>
        protected override unsafe void OnApply(ImageFrame <TPixel> source, ImageFrame <TPixel> cloned, Rectangle sourceRectangle, Configuration configuration)
        {
            // Jump out, we'll deal with that later.
            if (source.Width == cloned.Width && source.Height == cloned.Height && sourceRectangle == this.ResizeRectangle)
            {
                // the cloned will be blank here copy all the pixel data over
                source.GetPixelSpan().CopyTo(cloned.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)
            {
                // Scaling factors
                float widthFactor  = sourceRectangle.Width / (float)this.ResizeRectangle.Width;
                float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height;

                Parallel.For(
                    minY,
                    maxY,
                    configuration.ParallelOptions,
                    y =>
                {
                    // Y coordinates of source points
                    Span <TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
                    Span <TPixel> targetRow = cloned.GetPixelRowSpan(y);

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

                return;
            }

            // 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.
            // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed!
            using (var firstPassPixels = new Buffer2D <Vector4>(width, source.Height))
            {
                firstPassPixels.Clear();

                Parallel.For(
                    0,
                    sourceRectangle.Bottom,
                    configuration.ParallelOptions,
                    y =>
                {
                    // TODO: Without Parallel.For() this buffer object could be reused:
                    using (var tempRowBuffer = new Buffer <Vector4>(source.Width))
                    {
                        Span <Vector4> firstPassRow = firstPassPixels.GetRowSpan(y);
                        Span <TPixel> sourceRow     = source.GetPixelRowSpan(y);
                        PixelOperations <TPixel> .Instance.ToVector4(sourceRow, tempRowBuffer, sourceRow.Length);

                        if (this.Compand)
                        {
                            for (int x = minX; x < maxX; x++)
                            {
                                WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
                                firstPassRow[x]      = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX);
                            }
                        }
                        else
                        {
                            for (int x = minX; x < maxX; x++)
                            {
                                WeightsWindow window = this.HorizontalWeights.Weights[x - startX];
                                firstPassRow[x]      = window.ComputeWeightedRowSum(tempRowBuffer, sourceX);
                            }
                        }
                    }
                });

                // Now process the rows.
                Parallel.For(
                    minY,
                    maxY,
                    configuration.ParallelOptions,
                    y =>
                {
                    // Ensure offsets are normalised for cropping and padding.
                    WeightsWindow window    = this.VerticalWeights.Weights[y - startY];
                    Span <TPixel> targetRow = cloned.GetPixelRowSpan(y);

                    if (this.Compand)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Destination color components
                            Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);
                            destination         = destination.Compress();

                            ref TPixel pixel = ref targetRow[x];
                            pixel.PackFromVector4(destination);
                        }
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            // Destination color components
                            Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x, sourceY);

                            ref TPixel pixel = ref targetRow[x];
                            pixel.PackFromVector4(destination);
                        }
                    }
                });