private void RestoreToBackground(ImageBase <TColor, TPacked> frame) { if (this.restoreArea == null) { return; } // Optimization for when the size of the frame is the same as the image size. if (this.restoreArea.Value.Width == this.decodedImage.Width && this.restoreArea.Value.Height == this.decodedImage.Height) { using (PixelAccessor <TColor, TPacked> pixelAccessor = frame.Lock()) { pixelAccessor.Reset(); } } else { using (PixelArea <TColor, TPacked> emptyRow = new PixelArea <TColor, TPacked>(this.restoreArea.Value.Width, ComponentOrder.XYZW)) { using (PixelAccessor <TColor, TPacked> pixelAccessor = frame.Lock()) { for (int y = this.restoreArea.Value.Top; y < this.restoreArea.Value.Top + this.restoreArea.Value.Height; y++) { pixelAccessor.CopyFrom(emptyRow, y, this.restoreArea.Value.Left); } } } } this.restoreArea = null; }
/// <summary> /// Collects the true color pixel data. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The image to encode.</param> private void CollectColorBytes <TColor, TPacked>(ImageBase <TColor, TPacked> image) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // Copy the pixels across from the image. // TODO: This could be sped up more if we add a method to PixelAccessor that does this by row directly to a byte array. this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; int stride = this.width * this.bytesPerPixel; using (PixelAccessor <TColor, TPacked> pixels = image.Lock()) { int bpp = this.bytesPerPixel; Parallel.For( 0, this.height, Bootstrapper.Instance.ParallelOptions, y => { for (int x = 0; x < this.width; x++) { int dataOffset = (y * stride) + (x * this.bytesPerPixel); pixels[x, y].ToBytes(this.pixelData, dataOffset, bpp == 4 ? ComponentOrder.XYZW : ComponentOrder.XYZ); } }); } }
/// <inheritdoc/> public override void Apply(ImageBase <TColor, TPacked> target, ImageBase <TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { if (this.OptimizedApply(target, source)) { return; } int height = target.Height; int width = target.Width; Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { Point transformedPoint = Point.Rotate(new Point(x, y), matrix); if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } } }); } }
/// <summary> /// Rotates the image 90 degrees clockwise at the centre point. /// </summary> /// <param name="target">The target image.</param> /// <param name="source">The source image.</param> private void Rotate90(ImageBase <TColor, TPacked> target, ImageBase <TColor, TPacked> source) { int width = source.Width; int height = source.Height; Image <TColor, TPacked> temp = new Image <TColor, TPacked>(height, width); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> tempPixels = temp.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = height - y - 1; tempPixels[newX, x] = sourcePixels[x, y]; } }); } target.SetPixels(height, width, temp.Pixels); }
/// <summary> /// Swaps the image at the X-axis, which goes horizontally through the middle /// at half the height of the image. /// </summary> /// <param name="target">Target image to apply the process to.</param> private void FlipX(ImageBase <T, TP> target) { int width = target.Width; int height = target.Height; int halfHeight = (int)Math.Ceiling(target.Height * .5F); Image <T, TP> temp = new Image <T, TP>(width, height); temp.ClonePixels(width, height, target.Pixels); using (IPixelAccessor <T, TP> targetPixels = target.Lock()) using (IPixelAccessor <T, TP> tempPixels = temp.Lock()) { Parallel.For( 0, halfHeight, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newY = height - y - 1; targetPixels[x, y] = tempPixels[x, newY]; targetPixels[x, newY] = tempPixels[x, y]; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> public override void Apply(ImageBase <TColor, TPacked> target, ImageBase <TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. if (source.Bounds == target.Bounds) { return; } int targetY = this.cropRectangle.Y; int targetBottom = this.cropRectangle.Bottom; int startX = this.cropRectangle.X; int endX = this.cropRectangle.Right; int minY = Math.Max(targetY, startY); int maxY = Math.Min(targetBottom, endY); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { targetPixels[x - startX, y - targetY] = sourcePixels[x, y]; } }); } }
/// <summary> /// Swaps the image at the Y-axis, which goes vertically through the middle /// at half of the width of the image. /// </summary> /// <param name="source">The source image to apply the process to.</param> private void FlipY(ImageBase <TColor> source) { int width = source.Width; int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); using (PixelAccessor <TColor> targetPixels = new PixelAccessor <TColor>(width, height)) { using (PixelAccessor <TColor> sourcePixels = source.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < halfWidth; x++) { int newX = width - x - 1; targetPixels[x, y] = sourcePixels[newX, y]; targetPixels[newX, y] = sourcePixels[x, y]; } }); } source.SwapPixelsBuffers(targetPixels); } }
/// <inheritdoc/> public override void Apply(ImageBase <TColor, TPacked> target, ImageBase <TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = targetRectangle.X; int endX = targetRectangle.Right; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; Guard.MustBeGreaterThanOrEqualTo(startX, sourceX, nameof(targetRectangle)); Guard.MustBeGreaterThanOrEqualTo(startY, sourceY, nameof(targetRectangle)); Guard.MustBeLessThanOrEqualTo(endX, sourceRectangle.Right, nameof(targetRectangle)); Guard.MustBeLessThanOrEqualTo(endY, sourceRectangle.Bottom, nameof(targetRectangle)); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock()) { Parallel.For( startY, endY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; } }); } }
/// <summary> /// Swaps the image at the Y-axis, which goes vertically through the middle /// at half of the width of the image. /// </summary> /// <param name="source">The source image to apply the process to.</param> private void FlipY(ImageBase <TColor, TPacked> source) { int width = source.Width; int height = source.Height; int halfWidth = (int)Math.Ceiling(width * .5F); TColor[] target = new TColor[width * height]; using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock <TColor, TPacked>(width, height)) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < halfWidth; x++) { int newX = width - x - 1; targetPixels[x, y] = sourcePixels[newX, y]; targetPixels[newX, y] = sourcePixels[x, y]; } }); } source.SetPixels(width, height, target); }
/// <summary> /// Collects the true color pixel data. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The image to encode.</param> private void CollectColorBytes <TColor, TPacked>(ImageBase <TColor, TPacked> image) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // Copy the pixels across from the image. this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; int stride = this.width * this.bytesPerPixel; using (PixelAccessor <TColor, TPacked> pixels = image.Lock()) { Parallel.For( 0, this.height, Bootstrapper.Instance.ParallelOptions, y => { for (int x = 0; x < this.width; x++) { int dataOffset = (y * stride) + (x * this.bytesPerPixel); Color source = new Color(pixels[x, y].ToVector4()); this.pixelData[dataOffset] = source.R; this.pixelData[dataOffset + 1] = source.G; this.pixelData[dataOffset + 2] = source.B; if (this.bytesPerPixel == 4) { this.pixelData[dataOffset + 3] = source.A; } } }); } }
/// <summary> /// Rotates the image 180 degrees clockwise at the centre point. /// </summary> /// <param name="target">The target image.</param> /// <param name="source">The source image.</param> private void Rotate180(ImageBase <T, TP> target, ImageBase <T, TP> source) { int width = source.Width; int height = source.Height; using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = width - x - 1; int newY = height - y - 1; targetPixels[newX, newY] = sourcePixels[x, y]; } this.OnRowProcessed(); }); } }
/// <summary> /// Rotates the image 270 degrees clockwise at the centre point. /// </summary> /// <param name="target">The target image.</param> /// <param name="source">The source image.</param> private void Rotate270(ImageBase <T, TP> target, ImageBase <T, TP> source) { int width = source.Width; int height = source.Height; Image <T, TP> temp = new Image <T, TP>(height, width); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> tempPixels = temp.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = height - y - 1; newX = height - newX - 1; int newY = width - x - 1; tempPixels[newX, newY] = sourcePixels[x, y]; } this.OnRowProcessed(); }); } target.SetPixels(height, width, temp.Pixels); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor> source, Rectangle sourceRectangle) { if (this.CropRectangle == sourceRectangle) { return; } int minY = Math.Max(this.CropRectangle.Y, sourceRectangle.Y); int maxY = Math.Min(this.CropRectangle.Bottom, sourceRectangle.Bottom); int minX = Math.Max(this.CropRectangle.X, sourceRectangle.X); int maxX = Math.Min(this.CropRectangle.Right, sourceRectangle.Right); TColor[] target = new TColor[this.CropRectangle.Width * this.CropRectangle.Height]; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> targetPixels = target.Lock <TColor>(this.CropRectangle.Width, this.CropRectangle.Height)) { Parallel.For( minY, maxY, this.ParallelOptions, y => { for (int x = minX; x < maxX; x++) { targetPixels[x - minX, y - minY] = sourcePixels[x, y]; } }); } source.SetPixels(this.CropRectangle.Width, this.CropRectangle.Height, target); }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); TColor[] target = new TColor[width * height]; using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock <TColor, TPacked>(width, height)) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { Point transformedPoint = Point.Skew(new Point(x, y), matrix); if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } } }); } source.SetPixels(width, height, target); }
/// <summary> /// Swaps the image at the X-axis, which goes horizontally through the middle /// at half the height of the image. /// </summary> /// <param name="source">The source image to apply the process to.</param> private void FlipX(ImageBase <TColor> source) { int width = source.Width; int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); TColor[] target = new TColor[width * height]; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> targetPixels = target.Lock <TColor>(width, height)) { Parallel.For( 0, halfHeight, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newY = height - y - 1; targetPixels[x, y] = sourcePixels[x, newY]; targetPixels[x, newY] = sourcePixels[x, y]; } }); } source.SetPixels(width, height, target); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int height = target.Height; int width = target.Width; Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { Point transformedPoint = Point.Skew(new Point(x, y), matrix); if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } } OnRowProcessed(); }); } }
/// <summary> /// Collects the true color pixel data. /// </summary> /// <typeparam name="T">The pixel format.</typeparam> /// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam> /// <param name="image">The image to encode.</param> private void CollectColorBytes <T, TP>(ImageBase <T, TP> image) where T : IPackedVector <TP> where TP : struct { // Copy the pixels across from the image. this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; int stride = this.width * this.bytesPerPixel; using (IPixelAccessor <T, TP> pixels = image.Lock()) { Parallel.For( 0, this.height, Bootstrapper.Instance.ParallelOptions, y => { // Color data is stored in r -> g -> b -> a order for (int x = 0; x < this.width; x++) { int dataOffset = (y * stride) + (x * this.bytesPerPixel); byte[] source = pixels[x, y].ToBytes(); for (int i = 0; i < this.bytesPerPixel; i++) { this.pixelData[dataOffset + i] = source[i]; } } }); } }
/// <summary> /// Collects the grayscale pixel data. /// </summary> /// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <param name="image">The image to encode.</param> private void CollectGrayscaleBytes <TColor, TPacked>(ImageBase <TColor, TPacked> image) where TColor : struct, IPackedPixel <TPacked> where TPacked : struct { // Copy the pixels across from the image. this.pixelData = new byte[this.width * this.height * this.bytesPerPixel]; int stride = this.width * this.bytesPerPixel; byte[] bytes = new byte[4]; using (PixelAccessor <TColor, TPacked> pixels = image.Lock()) { for (int y = 0; y < this.height; y++) { for (int x = 0; x < this.width; x++) { // Convert the color to YCbCr and store the luminance // Optionally store the original color alpha. int dataOffset = (y * stride) + (x * this.bytesPerPixel); pixels[x, y].ToBytes(bytes, 0, ComponentOrder.XYZW); YCbCr luminance = new Color(bytes[0], bytes[1], bytes[2], bytes[3]); for (int i = 0; i < this.bytesPerPixel; i++) { if (i == 0) { this.pixelData[dataOffset] = (byte)luminance.Y; } else { this.pixelData[dataOffset + i] = bytes[3]; } } } } } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; TPixel glowColor = this.GlowColor; Vector2 centre = Rectangle.Center(sourceRectangle).ToVector2(); float maxDistance = this.Radius > 0 ? MathF.Min(this.Radius, sourceRectangle.Width * .5F) : sourceRectangle.Width * .5F; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } int width = maxX - minX; using (Buffer <TPixel> rowColors = new Buffer <TPixel>(width)) using (PixelAccessor <TPixel> sourcePixels = source.Lock()) { for (int i = 0; i < width; i++) { rowColors[i] = glowColor; } Parallel.For( minY, maxY, this.ParallelOptions, y => { using (Buffer <float> amounts = new Buffer <float>(width)) { int offsetY = y - startY; int offsetX = minX - startX; for (int i = 0; i < width; i++) { float distance = Vector2.Distance(centre, new Vector2(i + offsetX, offsetY)); amounts[i] = (this.options.BlendPercentage * (1 - (.95F * (distance / maxDistance)))).Clamp(0, 1); } Span <TPixel> destination = sourcePixels.GetRowSpan(offsetY).Slice(offsetX, width); this.blender.Blend(destination, destination, rowColors, amounts); } }); } }
/// <inheritdoc/> public virtual QuantizedImage <TColor> Quantize(ImageBase <TColor> image, int maxColors) { Guard.NotNull(image, nameof(image)); // Get the size of the source image int height = image.Height; int width = image.Width; byte[] quantizedPixels = new byte[width * height]; TColor[] palette; using (PixelAccessor <TColor> pixels = image.Lock()) { // Call the FirstPass function if not a single pass algorithm. // For something like an Octree quantizer, this will run through // all image pixels, build a data structure, and create a palette. if (!this.singlePass) { this.FirstPass(pixels, width, height); } // Get the palette palette = this.GetPalette(); this.SecondPass(pixels, quantizedPixels, width, height); } return(new QuantizedImage <TColor>(width, height, palette, quantizedPixels)); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = targetRectangle.X; int endX = targetRectangle.Right; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( startY, endY, this.ParallelOptions, y => { for (int x = startX; x < endX; x++) { targetPixels[x, y] = sourcePixels[x + sourceX, y + sourceY]; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TPixel> source, Rectangle sourceRectangle) { if (this.OptimizedApply(source)) { return; } int height = this.CanvasRectangle.Height; int width = this.CanvasRectangle.Width; Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); using (PixelAccessor <TPixel> targetPixels = new PixelAccessor <TPixel>(width, height)) { using (PixelAccessor <TPixel> sourcePixels = source.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { Point transformedPoint = Point.Rotate(new Point(x, y), matrix); if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y)) { targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y]; } } }); } source.SwapPixelsBuffers(targetPixels); } }
/// <summary> /// Rotates the image 270 degrees clockwise at the centre point. /// </summary> /// <param name="source">The source image.</param> private void Rotate270(ImageBase <TPixel> source) { int width = source.Width; int height = source.Height; using (var targetPixels = new PixelAccessor <TPixel>(height, width)) { using (PixelAccessor <TPixel> sourcePixels = source.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = height - y - 1; newX = height - newX - 1; int newY = width - x - 1; targetPixels[newX, newY] = sourcePixels[x, y]; } }); } source.SwapPixelsBuffers(targetPixels); } }
/// <summary> /// Swaps the image at the X-axis, which goes horizontally through the middle /// at half the height of the image. /// </summary> /// <param name="source">The source image to apply the process to.</param> private void FlipX(ImageBase <TPixel> source) { int width = source.Width; int height = source.Height; int halfHeight = (int)Math.Ceiling(source.Height * .5F); using (PixelAccessor <TPixel> targetPixels = new PixelAccessor <TPixel>(width, height)) { using (PixelAccessor <TPixel> sourcePixels = source.Lock()) { Parallel.For( 0, halfHeight, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newY = height - y - 1; targetPixels[x, y] = sourcePixels[x, newY]; targetPixels[x, newY] = sourcePixels[x, y]; } }); } source.SwapPixelsBuffers(targetPixels); } }
/// <summary> /// Swaps the image at the Y-axis, which goes vertically through the middle /// at half of the width of the image. /// </summary> /// <param name="target">Target image to apply the process to.</param> private void FlipY(ImageBase <TColor, TPacked> target) { int width = target.Width; int height = target.Height; int halfWidth = (int)Math.Ceiling(width * .5F); Image <TColor, TPacked> temp = new Image <TColor, TPacked>(width, height); temp.ClonePixels(width, height, target.Pixels); using (PixelAccessor <TColor, TPacked> targetPixels = target.Lock()) using (PixelAccessor <TColor, TPacked> tempPixels = temp.Lock()) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < halfWidth; x++) { int newX = width - x - 1; targetPixels[x, y] = tempPixels[newX, y]; targetPixels[newX, y] = tempPixels[x, y]; } }); } }
/// <summary> /// Rotates the image 90 degrees clockwise at the centre point. /// </summary> /// <param name="source">The source image.</param> private void Rotate90(ImageBase <TColor> source) { int width = source.Width; int height = source.Height; TColor[] target = new TColor[width * height]; using (PixelAccessor <TColor> sourcePixels = source.Lock()) using (PixelAccessor <TColor> targetPixels = target.Lock <TColor>(height, width)) { Parallel.For( 0, height, this.ParallelOptions, y => { for (int x = 0; x < width; x++) { int newX = height - y - 1; targetPixels[newX, x] = sourcePixels[x, y]; } }); } source.SetPixels(height, width, target); }
/// <inheritdoc/> protected override void Apply(ImageBase <T, TP> target, ImageBase <T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } Vector4 backgroundColor = this.Value.ToVector4(); using (IPixelAccessor <T, TP> sourcePixels = source.Lock()) using (IPixelAccessor <T, TP> targetPixels = target.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); float a = color.W; if (a < 1 && a > 0) { color = Vector4.Lerp(color, backgroundColor, .5F); } if (Math.Abs(a) < Epsilon) { color = backgroundColor; } T packed = default(T); packed.PackFromVector4(color); targetPixels[offsetX, offsetY] = packed; } this.OnRowProcessed(); }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } // we could possibly do some optermising by having knowledge about the individual brushes operate // for example If brush is SolidBrush<TColor, TPacked> then we could just get the color upfront // and skip using the IBrushApplicator<TColor, TPacked>?. using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) using (IBrushApplicator <TColor, TPacked> applicator = this.brush.CreateApplicator(sourceRectangle)) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; Vector2 currentPoint = default(Vector2); for (int x = minX; x < maxX; x++) { int offsetX = x - startX; int offsetColorX = x - minX; currentPoint.X = offsetX; currentPoint.Y = offsetY; Vector4 backgroundVector = sourcePixels[offsetX, offsetY].ToVector4(); Vector4 sourceVector = applicator.GetColor(currentPoint).ToVector4(); var finalColor = Vector4BlendTransforms.PremultipliedLerp(backgroundVector, sourceVector, 1); TColor packed = default(TColor); packed.PackFromVector4(finalColor); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void OnApply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle) { int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; int startX = sourceRectangle.X; int endX = sourceRectangle.Right; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } Vector4 backgroundColor = this.Value.ToVector4(); using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); float a = color.W; if (a < 1 && a > 0) { color = Vector4BlendTransforms.PremultipliedLerp(backgroundColor, color, .5F); } if (Math.Abs(a) < Epsilon) { color = backgroundColor; } TColor packed = default(TColor); packed.PackFromVector4(color); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <inheritdoc/> protected override void Apply(ImageBase <TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY) { int startX = sourceRectangle.X; int endX = sourceRectangle.Right; Rectangle bounds = this.blend.Bounds; // Align start/end positions. int minX = Math.Max(0, startX); int maxX = Math.Min(source.Width, endX); int minY = Math.Max(0, startY); int maxY = Math.Min(source.Height, endY); // Reset offset if necessary. if (minX > 0) { startX = 0; } if (minY > 0) { startY = 0; } float alpha = this.Value / 100F; using (PixelAccessor <TColor, TPacked> toBlendPixels = this.blend.Lock()) using (PixelAccessor <TColor, TPacked> sourcePixels = source.Lock()) { Parallel.For( minY, maxY, this.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); if (bounds.Contains(offsetX, offsetY)) { Vector4 blendedColor = toBlendPixels[offsetX, offsetY].ToVector4(); if (blendedColor.W > 0) { // Lerping colors is dependent on the alpha of the blended color color = Vector4.Lerp(color, blendedColor, alpha > 0 ? alpha : blendedColor.W); } } TColor packed = default(TColor); packed.PackFromVector4(color); sourcePixels[offsetX, offsetY] = packed; } }); } }
public QuantizedImage Quantize(ImageBase image, int maxColors) { if (image == null){ throw new ArgumentNullException(); } int colorCount = NumUtils.Clamp(maxColors,1, 256); Clear(); using (IPixelAccessor imagePixels = image.Lock()){ Build3DHistogram(imagePixels); Get3DMoments(); Box[] cube; BuildCube(out cube, ref colorCount); return GenerateResult(imagePixels, colorCount, cube); } }
public void Encode(ImageBase image, Stream stream, int quality, JpegSubsample sample) { if (image == null || stream == null){ throw new ArgumentNullException(); } ushort max = JpegConstants.MaxLength; if (image.Width >= max || image.Height >= max){ throw new Exception($"Image is too large to encode at {image.Width}x{image.Height}."); } outputStream = stream; subsample = sample; for (int i = 0; i < theHuffmanSpec.Length; i++){ theHuffmanLut[i] = new HuffmanLut(theHuffmanSpec[i]); } for (int i = 0; i < nQuantIndex; i++){ quant[i] = new byte[Block.blockSize]; } if (quality < 1){ quality = 1; } if (quality > 100){ quality = 100; } int scale; if (quality < 50){ scale = 5000/quality; } else{ scale = 200 - quality*2; } for (int i = 0; i < nQuantIndex; i++){ for (int j = 0; j < Block.blockSize; j++){ int x = unscaledQuant[i, j]; x = (x*scale + 50)/100; if (x < 1){ x = 1; } if (x > 255){ x = 255; } quant[i][j] = (byte) x; } } int componentCount = 3; double densityX = ((Image2) image).HorizontalResolution; double densityY = ((Image2) image).VerticalResolution; WriteApplicationHeader((short) densityX, (short) densityY); WriteDqt(); WriteSof0(image.Width, image.Height, componentCount); WriteDht(componentCount); using (IPixelAccessor pixels = image.Lock()){ WriteSos(pixels); } buffer[0] = 0xff; buffer[1] = 0xd9; stream.Write(buffer, 0, 2); stream.Flush(); }
private void WriteImage(EndianBinaryWriter writer, ImageBase image) { // TODO: Add more compression formats. int amount = (image.Width*(int) bmpBitsPerPixel)%4; if (amount != 0){ amount = 4 - amount; } using (IPixelAccessor pixels = image.Lock()){ switch (bmpBitsPerPixel){ case BmpBitsPerPixel.Pixel32: Write32bit(writer, pixels, amount); break; case BmpBitsPerPixel.Pixel24: Write24bit(writer, pixels, amount); break; } } }