protected override void AfterImageApply(Image <TPixel> source, Image <TPixel> destination, Rectangle sourceRectangle)
        {
            base.AfterImageApply(source, destination, sourceRectangle);

            // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable!
            this.horizontalKernelMap?.Dispose();
            this.horizontalKernelMap = null;
            this.verticalKernelMap?.Dispose();
            this.verticalKernelMap = null;
        }
Esempio n. 2
0
        public ResizeWorker(
            Configuration configuration,
            Buffer2DRegion <TPixel> source,
            PixelConversionModifiers conversionModifiers,
            ResizeKernelMap horizontalKernelMap,
            ResizeKernelMap verticalKernelMap,
            int destWidth,
            Rectangle targetWorkingRect,
            Point targetOrigin)
        {
            this.configuration       = configuration;
            this.source              = source;
            this.sourceRectangle     = source.Rectangle;
            this.conversionModifiers = conversionModifiers;
            this.horizontalKernelMap = horizontalKernelMap;
            this.verticalKernelMap   = verticalKernelMap;
            this.destWidth           = destWidth;
            this.targetWorkingRect   = targetWorkingRect;
            this.targetOrigin        = targetOrigin;

            this.windowBandHeight = verticalKernelMap.MaxDiameter;

            // We need to make sure the working buffer is contiguous:
            int workingBufferLimitHintInBytes = Math.Min(
                configuration.WorkingBufferSizeHintInBytes,
                configuration.MemoryAllocator.GetBufferCapacityInBytes());

            int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(
                this.windowBandHeight,
                destWidth,
                workingBufferLimitHintInBytes);

            this.workerHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandHeight);

            this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D <Vector4>(
                this.workerHeight,
                destWidth,
                preferContiguosImageBuffers: true,
                options: AllocationOptions.Clean);

            this.tempRowBuffer    = configuration.MemoryAllocator.Allocate <Vector4>(this.sourceRectangle.Width);
            this.tempColumnBuffer = configuration.MemoryAllocator.Allocate <Vector4>(destWidth);

            this.currentWindow = new RowInterval(0, this.workerHeight);
        }
Esempio n. 3
0
        /// <inheritdoc/>
        protected override void Dispose(bool disposing)
        {
            if (this.isDisposed)
            {
                return;
            }

            if (disposing)
            {
                this.horizontalKernelMap?.Dispose();
                this.horizontalKernelMap = null;
                this.verticalKernelMap?.Dispose();
                this.verticalKernelMap = null;
            }

            this.isDisposed = true;
            base.Dispose(disposing);
        }
        /// <inheritdoc/>
        protected override void BeforeImageApply(Image <TPixel> source, Image <TPixel> destination, Rectangle sourceRectangle)
        {
            if (!(this.Sampler is NearestNeighborResampler))
            {
                // Since all image frame dimensions have to be the same we can calculate this for all frames.
                MemoryAllocator memoryAllocator = source.GetMemoryAllocator();
                this.horizontalKernelMap = ResizeKernelMap.Calculate(
                    this.Sampler,
                    this.TargetRectangle.Width,
                    sourceRectangle.Width,
                    memoryAllocator);

                this.verticalKernelMap = ResizeKernelMap.Calculate(
                    this.Sampler,
                    this.TargetRectangle.Height,
                    sourceRectangle.Height,
                    memoryAllocator);
            }
        }
Esempio n. 5
0
        public ResizeWorker(
            Configuration configuration,
            BufferArea <TPixel> source,
            PixelConversionModifiers conversionModifiers,
            ResizeKernelMap horizontalKernelMap,
            ResizeKernelMap verticalKernelMap,
            int destWidth,
            Rectangle targetWorkingRect,
            Point targetOrigin)
        {
            this.configuration       = configuration;
            this.source              = source;
            this.sourceRectangle     = source.Rectangle;
            this.conversionModifiers = conversionModifiers;
            this.horizontalKernelMap = horizontalKernelMap;
            this.verticalKernelMap   = verticalKernelMap;
            this.destWidth           = destWidth;
            this.targetWorkingRect   = targetWorkingRect;
            this.targetOrigin        = targetOrigin;

            this.windowBandHeight = verticalKernelMap.MaxDiameter;

            int numberOfWindowBands = ResizeHelper.CalculateResizeWorkerHeightInWindowBands(
                this.windowBandHeight,
                destWidth,
                configuration.WorkingBufferSizeHintInBytes);

            this.workerHeight = Math.Min(this.sourceRectangle.Height, numberOfWindowBands * this.windowBandHeight);

            this.transposedFirstPassBuffer = configuration.MemoryAllocator.Allocate2D <Vector4>(
                this.workerHeight,
                destWidth,
                AllocationOptions.Clean);

            this.tempRowBuffer    = configuration.MemoryAllocator.Allocate <Vector4>(this.sourceRectangle.Width);
            this.tempColumnBuffer = configuration.MemoryAllocator.Allocate <Vector4>(destWidth);

            this.currentWindow = new RowInterval(0, this.workerHeight);
        }
Esempio n. 6
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="ResizeKernelMap"/></returns>
        public static ResizeKernelMap Calculate(
            IResampler sampler,
            int destinationSize,
            int sourceSize,
            MemoryAllocator memoryAllocator)
        {
            double ratio = (double)sourceSize / destinationSize;
            double scale = ratio;

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

            int radius = (int)TolerantMath.Ceiling(scale * sampler.Radius);

            // 'ratio' is a rational number.
            // Multiplying it by LCM(sourceSize, destSize)/sourceSize will result in a whole number "again".
            // This value is determining the length of the periods in repeating kernel map rows.
            int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;

            // the center position at i == 0:
            double center0 = (ratio - 1) * 0.5;
            double firstNonNegativeLeftVal = (radius - center0 - 1) / ratio;

            // The number of rows building a "stairway" at the top and the bottom of the kernel map
            // corresponding to the corners of the image.
            // If we do not normalize the kernel values, these rows also fit the periodic logic,
            // however, it's just simpler to calculate them separately.
            int cornerInterval = (int)TolerantMath.Ceiling(firstNonNegativeLeftVal);

            // If firstNonNegativeLeftVal was an integral value, we need firstNonNegativeLeftVal+1
            // instead of Ceiling:
            if (TolerantMath.AreEqual(firstNonNegativeLeftVal, cornerInterval))
            {
                cornerInterval++;
            }

            // If 'cornerInterval' is too big compared to 'period', we can't apply the periodic optimization.
            // If we don't have at least 2 periods, we go with the basic implementation:
            bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize;

            ResizeKernelMap result = hasAtLeast2Periods
                                         ? new PeriodicKernelMap(
                memoryAllocator,
                sampler,
                sourceSize,
                destinationSize,
                ratio,
                scale,
                radius,
                period,
                cornerInterval)
                                         : new ResizeKernelMap(
                memoryAllocator,
                sampler,
                sourceSize,
                destinationSize,
                destinationSize,
                ratio,
                scale,
                radius);

            result.Initialize();

            return(result);
        }