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; }
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); }
/// <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); } }
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); }
/// <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); }