/// <summary> /// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class. /// </summary> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="source">The source.</param> internal ImageFrame(MemoryManager memoryManager, ImageFrame <TPixel> source) { this.MemoryManager = memoryManager; this.PixelBuffer = memoryManager.Allocate2D <TPixel>(source.PixelBuffer.Width, source.PixelBuffer.Height); source.PixelBuffer.Span.CopyTo(this.PixelBuffer.Span); this.MetaData = source.MetaData.Clone(); }
/// <summary> /// Initializes <see cref="SpectralBlocks"/> /// </summary> /// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param> /// <param name="decoder">The <see cref="OrigJpegDecoderCore"/> instance</param> public void InitializeDerivedData(MemoryManager memoryManager, OrigJpegDecoderCore decoder) { // For 4-component images (either CMYK or YCbCrK), we only support two // hv vectors: [0x11 0x11 0x11 0x11] and [0x22 0x11 0x11 0x22]. // Theoretically, 4-component JPEG images could mix and match hv values // but in practice, those two combinations are the only ones in use, // and it simplifies the applyBlack code below if we can assume that: // - for CMYK, the C and K channels have full samples, and if the M // and Y channels subsample, they subsample both horizontally and // vertically. // - for YCbCrK, the Y and K channels have full samples. this.SizeInBlocks = decoder.ImageSizeInMCU.MultiplyBy(this.SamplingFactors); if (this.Index == 0 || this.Index == 3) { this.SubSamplingDivisors = new Size(1, 1); } else { OrigComponent c0 = decoder.Components[0]; this.SubSamplingDivisors = c0.SamplingFactors.DivideBy(this.SamplingFactors); } this.SpectralBlocks = memoryManager.Allocate2D <Block8x8>(this.SizeInBlocks.Width, this.SizeInBlocks.Height, true); }
/// <summary> /// TODO: This should be a common processing method! The image.Opacity(val) multiplies the alpha channel! /// </summary> /// <typeparam name="TPixel"></typeparam> /// <param name="ctx"></param> public static void MakeOpaque <TPixel>(this IImageProcessingContext <TPixel> ctx) where TPixel : struct, IPixel <TPixel> { MemoryManager memoryManager = ctx.MemoryManager; ctx.Apply(img => { using (Buffer2D <Vector4> temp = memoryManager.Allocate2D <Vector4>(img.Width, img.Height)) { Span <Vector4> tempSpan = temp.Span; foreach (ImageFrame <TPixel> frame in img.Frames) { Span <TPixel> pixelSpan = frame.GetPixelSpan(); PixelOperations <TPixel> .Instance.ToVector4(pixelSpan, tempSpan, pixelSpan.Length); for (int i = 0; i < tempSpan.Length; i++) { ref Vector4 v = ref tempSpan[i]; v.W = 1.0f; } PixelOperations <TPixel> .Instance.PackFromVector4(tempSpan, pixelSpan, pixelSpan.Length); } } });
/// <summary> /// Initializes a new instance of the <see cref="JpegComponentPostProcessor"/> class. /// </summary> public JpegComponentPostProcessor(MemoryManager memoryManager, JpegImagePostProcessor imagePostProcessor, IJpegComponent component) { this.Component = component; this.ImagePostProcessor = imagePostProcessor; this.ColorBuffer = memoryManager.Allocate2D <float>( imagePostProcessor.PostProcessorBufferSize.Width, imagePostProcessor.PostProcessorBufferSize.Height); this.BlockRowsPerStep = JpegImagePostProcessor.BlockRowsPerStep / this.Component.SubSamplingDivisors.Height; this.blockAreaSize = this.Component.SubSamplingDivisors * 8; }
/// <summary> /// Initializes a new instance of the <see cref="WeightsBuffer"/> class. /// </summary> /// <param name="memoryManager">The MemoryManager to use for allocations.</param> /// <param name="sourceSize">The size of the source window</param> /// <param name="destinationSize">The size of the destination window</param> public WeightsBuffer(MemoryManager memoryManager, int sourceSize, int destinationSize) { this.dataBuffer = memoryManager.Allocate2D <float>(sourceSize, destinationSize, true); this.Weights = new WeightsWindow[destinationSize]; }
/// <inheritdoc/> protected override void OnFrameApply( ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration) { int height = this.TargetDimensions.Height; int width = this.TargetDimensions.Width; Rectangle sourceBounds = source.Bounds(); var targetBounds = new Rectangle(0, 0, width, height); // Since could potentially be resizing the canvas we might need to re-calculate the matrix Matrix3x2 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); // Convert from screen to world space. Matrix3x2.Invert(matrix, out matrix); if (this.Sampler is NearestNeighborResampler) { Parallel.For( 0, height, configuration.ParallelOptions, y => { Span <TPixel> destRow = destination.GetPixelRowSpan(y); for (int x = 0; x < width; x++) { var point = Point.Transform(new Point(x, y), matrix); if (sourceBounds.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } } }); return; } int maxSourceX = source.Width - 1; int maxSourceY = source.Height - 1; (float radius, float scale, float ratio)xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); (float radius, float scale, float ratio)yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); float xScale = xRadiusScale.scale; float yScale = yRadiusScale.scale; var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius); IResampler sampler = this.Sampler; var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); MemoryManager memoryManager = configuration.MemoryManager; using (Buffer2D <float> yBuffer = memoryManager.Allocate2D <float>(yLength, height)) using (Buffer2D <float> xBuffer = memoryManager.Allocate2D <float>(xLength, height)) { Parallel.For( 0, height, configuration.ParallelOptions, y => { ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y)); ref float ySpanRef = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y)); ref float xSpanRef = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y));
public PdfJsQuantizationTables(MemoryManager memoryManager) { this.Tables = memoryManager.Allocate2D <short>(64, 4); }
/// <inheritdoc/> protected override void OnApply(ImageFrame <TPixel> source, ImageFrame <TPixel> destination, Rectangle sourceRectangle, Configuration configuration) { int height = this.TargetDimensions.Height; int width = this.TargetDimensions.Width; Rectangle sourceBounds = source.Bounds(); var targetBounds = new Rectangle(0, 0, width, height); // Since could potentially be resizing the canvas we might need to re-calculate the matrix Matrix4x4 matrix = this.GetProcessingMatrix(sourceBounds, targetBounds); // Convert from screen to world space. Matrix4x4.Invert(matrix, out matrix); if (this.Sampler is NearestNeighborResampler) { Parallel.For( 0, height, configuration.ParallelOptions, y => { Span <TPixel> destRow = destination.GetPixelRowSpan(y); for (int x = 0; x < width; x++) { var point = Point.Round(Vector2.Transform(new Vector2(x, y), matrix)); if (sourceBounds.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } } }); return; } int maxSourceX = source.Width - 1; int maxSourceY = source.Height - 1; (float radius, float scale, float ratio)xRadiusScale = this.GetSamplingRadius(source.Width, destination.Width); (float radius, float scale, float ratio)yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height); float xScale = xRadiusScale.scale; float yScale = yRadiusScale.scale; var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius); IResampler sampler = this.Sampler; var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY); int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); MemoryManager memoryManager = configuration.MemoryManager; using (Buffer2D <float> yBuffer = memoryManager.Allocate2D <float>(yLength, height)) using (Buffer2D <float> xBuffer = memoryManager.Allocate2D <float>(xLength, height)) { Parallel.For( 0, height, configuration.ParallelOptions, y => { Span <TPixel> destRow = destination.GetPixelRowSpan(y); Span <float> ySpan = yBuffer.GetRowSpan(y); Span <float> xSpan = xBuffer.GetRowSpan(y); for (int x = 0; x < width; x++) { // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. var point = Vector2.Transform(new Vector2(x, y), matrix); // Clamp sampling pixel radial extents to the source image edges Vector2 maxXY = point + radius; Vector2 minXY = point - radius; // max, maxY, minX, minY var extents = new Vector4( MathF.Floor(maxXY.X + .5F), MathF.Floor(maxXY.Y + .5F), MathF.Ceiling(minXY.X - .5F), MathF.Ceiling(minXY.Y - .5F)); int right = (int)extents.X; int bottom = (int)extents.Y; int left = (int)extents.Z; int top = (int)extents.W; extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); int maxX = (int)extents.X; int maxY = (int)extents.Y; int minX = (int)extents.Z; int minY = (int)extents.W; if (minX == maxX || minY == maxY) { continue; } // It appears these have to be calculated on-the-fly. // Precalulating transformed weights would require prior knowledge of every transformed pixel location // since they can be at sub-pixel positions on both axis. // I've optimized where I can but am always open to suggestions. if (yScale > 1 && xScale > 1) { CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan); CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan); } else { CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan); CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan); } // Now multiply the results against the offsets Vector4 sum = Vector4.Zero; for (int yy = 0, j = minY; j <= maxY; j++, yy++) { float yWeight = ySpan[yy]; for (int xx = 0, i = minX; i <= maxX; i++, xx++) { float xWeight = xSpan[xx]; var vector = source[i, j].ToVector4(); // Values are first premultiplied to prevent darkening of edge pixels Vector4 multiplied = vector.Premultiply(); sum += multiplied * xWeight * yWeight; } } ref TPixel dest = ref destRow[x]; // Reverse the premultiplication dest.PackFromVector4(sum.UnPremultiply()); } }); } }