public override Image <TPixel> GetImage() { var result = new Image <TPixel>(this.Configuration, this.Width, this.Height); TPixel topLeftColor = Color.Red.ToPixel <TPixel>(); TPixel topRightColor = Color.Green.ToPixel <TPixel>(); TPixel bottomLeftColor = Color.Blue.ToPixel <TPixel>(); // Transparent purple: TPixel bottomRightColor = default; bottomRightColor.FromVector4(new Vector4(1f, 0f, 1f, 0.5f)); int midY = this.Height / 2; int midX = this.Width / 2; Buffer2D <TPixel> imageBuffer = result.GetRootFramePixelBuffer(); for (int y = 0; y < midY; y++) { Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y); row.Slice(0, midX).Fill(topLeftColor); row.Slice(midX, this.Width - midX).Fill(topRightColor); } for (int y = midY; y < this.Height; y++) { Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y); row.Slice(0, midX).Fill(bottomLeftColor); row.Slice(midX, this.Width - midX).Fill(bottomRightColor); } return(result); }
/// <summary> /// Apply an image integral. <See href="https://en.wikipedia.org/wiki/Summed-area_table"/> /// </summary> /// <param name="source">The image on which to apply the integral.</param> /// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <returns>The <see cref="Buffer2D{T}"/> containing all the sums.</returns> public static Buffer2D <ulong> CalculateIntegralImage <TPixel>(this Image <TPixel> source) where TPixel : unmanaged, IPixel <TPixel> { Configuration configuration = source.GetConfiguration(); int endY = source.Height; int endX = source.Width; Buffer2D <ulong> intImage = configuration.MemoryAllocator.Allocate2D <ulong>(source.Width, source.Height); ulong sumX0 = 0; Buffer2D <TPixel> sourceBuffer = source.Frames.RootFrame.PixelBuffer; using (IMemoryOwner <L8> tempRow = configuration.MemoryAllocator.Allocate <L8>(source.Width)) { Span <L8> tempSpan = tempRow.GetSpan(); Span <TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(0); Span <ulong> destRow = intImage.DangerousGetRowSpan(0); PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan); // First row for (int x = 0; x < endX; x++) { sumX0 += tempSpan[x].PackedValue; destRow[x] = sumX0; } Span <ulong> previousDestRow = destRow; // All other rows for (int y = 1; y < endY; y++) { sourceRow = sourceBuffer.DangerousGetRowSpan(y); destRow = intImage.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToL8(configuration, sourceRow, tempSpan); // Process first column sumX0 = tempSpan[0].PackedValue; destRow[0] = sumX0 + previousDestRow[0]; // Process all other colmns for (int x = 1; x < endX; x++) { sumX0 += tempSpan[x].PackedValue; destRow[x] = sumX0 + previousDestRow[x]; } previousDestRow = destRow; } } return(intImage); }
/// <summary> /// Reads a uncompressed TGA image where each pixels has 16 bit. /// </summary> /// <typeparam name="TPixel">The pixel type.</typeparam> /// <param name="width">The width of the image.</param> /// <param name="height">The height of the image.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="origin">The image origin.</param> private void ReadBgra16 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel <TPixel> { TPixel color = default; bool invertX = InvertX(origin); using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); Span <byte> rowSpan = row.GetSpan(); for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span <TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); if (invertX) { for (int x = width - 1; x >= 0; x--) { this.currentStream.Read(this.scratchBuffer, 0, 2); if (!this.hasAlpha) { this.scratchBuffer[1] |= 1 << 7; } if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { color.FromLa16(Unsafe.As <byte, La16>(ref this.scratchBuffer[0])); } else { color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref this.scratchBuffer[0])); } pixelSpan[x] = color; } } else { this.currentStream.Read(rowSpan); if (!this.hasAlpha) { // We need to set the alpha component value to fully opaque. for (int x = 1; x < rowSpan.Length; x += 2) { rowSpan[x] |= 1 << 7; } } if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) { PixelOperations <TPixel> .Instance.FromLa16Bytes(this.Configuration, rowSpan, pixelSpan, width); } else { PixelOperations <TPixel> .Instance.FromBgra5551Bytes(this.Configuration, rowSpan, pixelSpan, width); } } } }
public void ApplyQuantizationDither <TFrameQuantizer, TPixel>( ref TFrameQuantizer quantizer, ImageFrame <TPixel> source, IndexedImageFrame <TPixel> destination, Rectangle bounds) where TFrameQuantizer : struct, IQuantizer <TPixel> where TPixel : unmanaged, IPixel <TPixel> { if (this == default) { ThrowDefaultInstance(); } int spread = CalculatePaletteSpread(destination.Palette.Length); float scale = quantizer.Options.DitherScale; Buffer2D <TPixel> sourceBuffer = source.PixelBuffer; for (int y = bounds.Top; y < bounds.Bottom; y++) { ReadOnlySpan <TPixel> sourceRow = sourceBuffer.DangerousGetRowSpan(y).Slice(bounds.X, bounds.Width); Span <byte> destRow = destination.GetWritablePixelRowSpanUnsafe(y - bounds.Y).Slice(0, sourceRow.Length); for (int x = 0; x < sourceRow.Length; x++) { TPixel dithered = this.Dither(sourceRow[x], x, y, spread, scale); destRow[x] = quantizer.GetQuantizedColor(dithered, out TPixel _); } } }
private static void WriteGrayscale <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image) where TPixel : unmanaged, IPixel <TPixel> { int width = image.Width; int height = image.Height; Buffer2D <TPixel> pixelBuffer = image.PixelBuffer; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <L8> row = allocator.Allocate <L8>(width); Span <L8> rowSpan = row.GetSpan(); using IMemoryOwner <byte> plainMemory = allocator.Allocate <byte>(width * MaxCharsPerPixelGrayscale); Span <byte> plainSpan = plainMemory.GetSpan(); for (int y = 0; y < height; y++) { Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToL8( configuration, pixelSpan, rowSpan); int written = 0; for (int x = 0; x < width; x++) { Utf8Formatter.TryFormat(rowSpan[x].PackedValue, plainSpan.Slice(written), out int bytesWritten, DecimalFormat); written += bytesWritten; plainSpan[written++] = Space; } plainSpan[written - 1] = NewLine; stream.Write(plainSpan, 0, written); } }
/// <summary> /// Extract the alpha data of the image. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param> /// <param name="configuration">The global configuration.</param> /// <param name="memoryAllocator">The memory manager.</param> /// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns> private static IMemoryOwner <byte> ExtractAlphaChannel <TPixel>(Image <TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator) where TPixel : unmanaged, IPixel <TPixel> { Buffer2D <TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer; int height = image.Height; int width = image.Width; IMemoryOwner <byte> alphaDataBuffer = memoryAllocator.Allocate <byte>(width * height); Span <byte> alphaData = alphaDataBuffer.GetSpan(); using IMemoryOwner <Rgba32> rowBuffer = memoryAllocator.Allocate <Rgba32>(width); Span <Rgba32> rgbaRow = rowBuffer.GetSpan(); for (int y = 0; y < height; y++) { Span <TPixel> rowSpan = imageBuffer.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToRgba32(configuration, rowSpan, rgbaRow); int offset = y * width; for (int x = 0; x < width; x++) { alphaData[offset + x] = rgbaRow[x].A; } } return(alphaDataBuffer); }
/// <inheritdoc /> protected override void OnFrameApply(ImageFrame <TPixel> source) { var interest = Rectangle.Intersect(source.Bounds(), this.SourceRectangle); Configuration configuration = this.Configuration; using IQuantizer <TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer <TPixel>(configuration); using IndexedImageFrame <TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(source, interest); ReadOnlySpan <TPixel> paletteSpan = quantized.Palette.Span; int offsetY = interest.Top; int offsetX = interest.Left; Buffer2D <TPixel> sourceBuffer = source.PixelBuffer; for (int y = interest.Y; y < interest.Height; y++) { Span <TPixel> row = sourceBuffer.DangerousGetRowSpan(y); ReadOnlySpan <byte> quantizedRow = quantized.DangerousGetRowSpan(y - offsetY); for (int x = interest.Left; x < interest.Right; x++) { row[x] = paletteSpan[quantizedRow[x - offsetX]]; } } }
/// <summary> /// Writes an 8 bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// </summary> /// <typeparam name="TPixel">The type of the pixel.</typeparam> /// <param name="stream">The <see cref="Stream"/> to write to.</param> /// <param name="image"> The <see cref="ImageFrame{TPixel}"/> containing pixel data.</param> /// <param name="colorPalette">A byte span of size 1024 for the color palette.</param> private void Write8BitGray <TPixel>(Stream stream, ImageFrame <TPixel> image, Span <byte> colorPalette) where TPixel : unmanaged, IPixel <TPixel> { // Create a color palette with 256 different gray values. for (int i = 0; i <= 255; i++) { int idx = i * 4; byte grayValue = (byte)i; colorPalette[idx] = grayValue; colorPalette[idx + 1] = grayValue; colorPalette[idx + 2] = grayValue; // Padding byte, always 0. colorPalette[idx + 3] = 0; } stream.Write(colorPalette); Buffer2D <TPixel> imageBuffer = image.PixelBuffer; for (int y = image.Height - 1; y >= 0; y--) { ReadOnlySpan <TPixel> inputPixelRow = imageBuffer.DangerousGetRowSpan(y); ReadOnlySpan <byte> outputPixelRow = MemoryMarshal.AsBytes(inputPixelRow); stream.Write(outputPixelRow); for (int i = 0; i < this.padding; i++) { stream.WriteByte(0); } } }
/// <inheritdoc/> public override void Decode(IMemoryOwner <byte>[] data, Buffer2D <TPixel> pixels, int left, int top, int width, int height) { Span <byte> yData = data[0].GetSpan(); Span <byte> cbData = data[1].GetSpan(); Span <byte> crData = data[2].GetSpan(); if (this.ycbcrSubSampling != null && !(this.ycbcrSubSampling[0] == 1 && this.ycbcrSubSampling[1] == 1)) { ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], cbData, crData); } var color = default(TPixel); int offset = 0; int widthPadding = 0; if (this.ycbcrSubSampling != null) { // Round to the next integer multiple of horizontalSubSampling. widthPadding = TiffUtils.PaddingToNextInteger(width, this.ycbcrSubSampling[0]); } for (int y = top; y < top + height; y++) { Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { Rgba32 rgba = this.converter.ConvertToRgba32(yData[offset], cbData[offset], crData[offset]); color.FromRgba32(rgba); pixelRow[x] = color; offset++; } offset += widthPadding; } }
private static void WriteWideGrayscale <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image) where TPixel : unmanaged, IPixel <TPixel> { const int bytesPerPixel = 2; int width = image.Width; int height = image.Height; Buffer2D <TPixel> pixelBuffer = image.PixelBuffer; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <byte> row = allocator.Allocate <byte>(width * bytesPerPixel); Span <byte> rowSpan = row.GetSpan(); for (int y = 0; y < height; y++) { Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToL16Bytes( configuration, pixelSpan, rowSpan, width); stream.Write(rowSpan); } }
private static void WriteBlackAndWhite <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image) where TPixel : unmanaged, IPixel <TPixel> { int width = image.Width; int height = image.Height; Buffer2D <TPixel> pixelBuffer = image.PixelBuffer; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <L8> row = allocator.Allocate <L8>(width); Span <L8> rowSpan = row.GetSpan(); using IMemoryOwner <byte> plainMemory = allocator.Allocate <byte>(width * MaxCharsPerPixelBlackAndWhite); Span <byte> plainSpan = plainMemory.GetSpan(); for (int y = 0; y < height; y++) { Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToL8( configuration, pixelSpan, rowSpan); int written = 0; for (int x = 0; x < width; x++) { byte value = (rowSpan[x].PackedValue < 128) ? One : Zero; plainSpan[written++] = value; plainSpan[written++] = Space; } plainSpan[written - 1] = NewLine; stream.Write(plainSpan, 0, written); } }
public void WriteImagePixels_SingleImage() { using var image = new Image <L16>(256, 256); this.ProcessPixelRowsImpl(image, accessor => { for (int y = 0; y < accessor.Height; y++) { Span <L16> row = accessor.GetRowSpan(y); for (int x = 0; x < row.Length; x++) { row[x] = new L16((ushort)(x * y)); } } }); Buffer2D <L16> buffer = image.Frames.RootFrame.PixelBuffer; for (int y = 0; y < 256; y++) { Span <L16> row = buffer.DangerousGetRowSpan(y); for (int x = 0; x < 256; x++) { int actual = row[x].PackedValue; Assert.Equal(x * y, actual); } } }
/// <summary> /// Decodes pixel data using the current photometric interpretation. /// </summary> /// <param name="data">The buffers to read image data from.</param> /// <param name="pixels">The image buffer to write pixels to.</param> /// <param name="left">The x-coordinate of the left-hand side of the image block.</param> /// <param name="top">The y-coordinate of the top of the image block.</param> /// <param name="width">The width of the image block.</param> /// <param name="height">The height of the image block.</param> public override void Decode(IMemoryOwner <byte>[] data, Buffer2D <TPixel> pixels, int left, int top, int width, int height) { var color = default(TPixel); var rBitReader = new BitReader(data[0].GetSpan()); var gBitReader = new BitReader(data[1].GetSpan()); var bBitReader = new BitReader(data[2].GetSpan()); for (int y = top; y < top + height; y++) { Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width); for (int x = 0; x < pixelRow.Length; x++) { float r = rBitReader.ReadBits(this.bitsPerSampleR) / this.rFactor; float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor; float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor; color.FromVector4(new Vector4(r, g, b, 1.0f)); pixelRow[x] = color; } rBitReader.NextRow(); gBitReader.NextRow(); bBitReader.NextRow(); } }
/// <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> /// <param name="configuration">The configuration.</param> private void FlipX(Buffer2D <TPixel> source, Configuration configuration) { int height = source.Height; using IMemoryOwner <TPixel> tempBuffer = configuration.MemoryAllocator.Allocate <TPixel>(source.Width); Span <TPixel> temp = tempBuffer.Memory.Span; for (int yTop = 0; yTop < height / 2; yTop++) { int yBottom = height - yTop - 1; Span <TPixel> topRow = source.DangerousGetRowSpan(yBottom); Span <TPixel> bottomRow = source.DangerousGetRowSpan(yTop); topRow.CopyTo(temp); bottomRow.CopyTo(topRow); temp.CopyTo(bottomRow); } }
private void ReadBgra32Row <TPixel>(int width, Buffer2D <TPixel> pixels, Span <byte> row, int y) where TPixel : unmanaged, IPixel <TPixel> { this.currentStream.Read(row); Span <TPixel> pixelSpan = pixels.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.FromBgra32Bytes(this.Configuration, row, pixelSpan, width); }
private void CopyPixelRowFast( Buffer2D <TPixel> source, Span <Vector4> rowPixels, int x, int y, int tileWidth, Configuration configuration) => PixelOperations <TPixel> .Instance.ToVector4(configuration, source.DangerousGetRowSpan(y).Slice(start: x, length: tileWidth), rowPixels);
public void WriteImagePixels_MultiImage3() { using var img1 = new Image <L16>(256, 256); Buffer2D <L16> buffer2 = img1.Frames.RootFrame.PixelBuffer; for (int y = 0; y < 256; y++) { Span <L16> row = buffer2.DangerousGetRowSpan(y); for (int x = 0; x < 256; x++) { row[x] = new L16((ushort)(x * y)); } } using var img2 = new Image <L16>(256, 256); using var img3 = new Image <L16>(256, 256); this.ProcessPixelRowsImpl(img1, img2, img3, (accessor1, accessor2, accessor3) => { for (int y = 0; y < accessor1.Height; y++) { Span <L16> row1 = accessor1.GetRowSpan(y); Span <L16> row2 = accessor2.GetRowSpan(accessor2.Height - y - 1); Span <L16> row3 = accessor3.GetRowSpan(y); row1.CopyTo(row2); row1.CopyTo(row3); } }); buffer2 = img2.Frames.RootFrame.PixelBuffer; Buffer2D <L16> buffer3 = img3.Frames.RootFrame.PixelBuffer; for (int y = 0; y < 256; y++) { Span <L16> row2 = buffer2.DangerousGetRowSpan(y); Span <L16> row3 = buffer3.DangerousGetRowSpan(y); for (int x = 0; x < 256; x++) { int actual2 = row2[x].PackedValue; int actual3 = row3[x].PackedValue; Assert.Equal(x * (256 - y - 1), actual2); Assert.Equal(x * y, actual3); } } }
public void ClearSpectralBuffers() { Buffer2D <Block8x8> spectralBlocks = this.component.SpectralBlocks; for (int i = 0; i < spectralBlocks.Height; i++) { spectralBlocks.DangerousGetRowSpan(i).Clear(); } }
/// <inheritdoc/> public void AddPaletteColors(Buffer2DRegion <TPixel> pixelRegion) { Rectangle bounds = pixelRegion.Rectangle; Buffer2D <TPixel> source = pixelRegion.Buffer; using (IMemoryOwner <Rgba32> buffer = this.Configuration.MemoryAllocator.Allocate <Rgba32>(bounds.Width)) { Span <Rgba32> bufferSpan = buffer.GetSpan(); // Loop through each row for (int y = bounds.Top; y < bounds.Bottom; y++) { Span <TPixel> row = source.DangerousGetRowSpan(y).Slice(bounds.Left, bounds.Width); PixelOperations <TPixel> .Instance.ToRgba32(this.Configuration, row, bufferSpan); for (int x = 0; x < bufferSpan.Length; x++) { Rgba32 rgba = bufferSpan[x]; // Add the color to the Octree this.octree.AddColor(rgba); } } } int paletteIndex = 0; Span <TPixel> paletteSpan = this.paletteOwner.GetSpan(); // On very rare occasions, (blur.png), the quantizer does not preserve a // transparent entry when palletizing the captured colors. // To workaround this we ensure the palette ends with the default color // for higher bit depths. Lower bit depths will correctly reduce the palette. // TODO: Investigate more evenly reduced palette reduction. int max = this.maxColors; if (this.bitDepth == 8) { max--; } this.octree.Palletize(paletteSpan, max, ref paletteIndex); ReadOnlyMemory <TPixel> result = this.paletteOwner.Memory.Slice(0, paletteSpan.Length); // When called multiple times by QuantizerUtilities.BuildPalette // this prevents memory churn caused by reallocation. if (this.pixelMap is null) { this.pixelMap = new EuclideanPixelMap <TPixel>(this.Configuration, result); } else { this.pixelMap.Clear(result); } this.palette = result; }
public override ImageSimilarityReport <TPixelA, TPixelB> CompareImagesOrFrames <TPixelA, TPixelB>(ImageFrame <TPixelA> expected, ImageFrame <TPixelB> actual) { if (expected.Size() != actual.Size()) { throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); } int width = actual.Width; // TODO: Comparing through Rgba64 may not robust enough because of the existence of super high precision pixel types. var aBuffer = new Rgba64[width]; var bBuffer = new Rgba64[width]; float totalDifference = 0F; var differences = new List <PixelDifference>(); Configuration configuration = expected.GetConfiguration(); Buffer2D <TPixelA> expectedBuffer = expected.PixelBuffer; Buffer2D <TPixelB> actualBuffer = actual.PixelBuffer; for (int y = 0; y < actual.Height; y++) { Span <TPixelA> aSpan = expectedBuffer.DangerousGetRowSpan(y); Span <TPixelB> bSpan = actualBuffer.DangerousGetRowSpan(y); PixelOperations <TPixelA> .Instance.ToRgba64(configuration, aSpan, aBuffer); PixelOperations <TPixelB> .Instance.ToRgba64(configuration, bSpan, bBuffer); for (int x = 0; x < width; x++) { int d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]); if (d > this.PerPixelManhattanThreshold) { var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]); differences.Add(diff); totalDifference += d; } } } float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height); normalizedDifference /= 4F * 65535F; if (normalizedDifference > this.ImageThreshold) { return(new ImageSimilarityReport <TPixelA, TPixelB>(expected, actual, differences, normalizedDifference)); } else { return(ImageSimilarityReport <TPixelA, TPixelB> .Empty); } }
private static void CopyImageBytesToBuffer(Span <byte> buffer, Buffer2D <Rgb24> pixelBuffer) { int offset = 0; for (int y = 0; y < pixelBuffer.Height; y++) { Span <Rgb24> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y); Span <byte> rgbBytes = MemoryMarshal.AsBytes(pixelRowSpan); rgbBytes.CopyTo(buffer.Slice(offset)); offset += rgbBytes.Length; } }
public void LoadSpectral(JpegComponent c) { Buffer2D <Block8x8> data = c.SpectralBlocks; for (int y = 0; y < this.HeightInBlocks; y++) { Span <Block8x8> blockRow = data.DangerousGetRowSpan(y); for (int x = 0; x < this.WidthInBlocks; x++) { this.MakeBlock(blockRow[x], y, x); } } }
/// <summary> /// Returns an image from the given System.Drawing bitmap. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="bmp">The input bitmap.</param> /// <exception cref="ArgumentException">Thrown if the image pixel format is not of type <see cref="PixelFormat.Format24bppRgb"/></exception> internal static unsafe Image <TPixel> From24bppRgbSystemDrawingBitmap <TPixel>(Bitmap bmp) where TPixel : unmanaged, IPixel <TPixel> { int w = bmp.Width; int h = bmp.Height; var fullRect = new System.Drawing.Rectangle(0, 0, w, h); if (bmp.PixelFormat != PixelFormat.Format24bppRgb) { throw new ArgumentException( $"{nameof(From24bppRgbSystemDrawingBitmap)}: pixel format should be {PixelFormat.Format24bppRgb}!", nameof(bmp)); } BitmapData data = bmp.LockBits(fullRect, ImageLockMode.ReadWrite, bmp.PixelFormat); var image = new Image <TPixel>(w, h); try { byte *sourcePtrBase = (byte *)data.Scan0; long sourceRowByteCount = data.Stride; long destRowByteCount = w * sizeof(Bgr24); Configuration configuration = image.GetConfiguration(); Buffer2D <TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer; using (IMemoryOwner <Bgr24> workBuffer = Configuration.Default.MemoryAllocator.Allocate <Bgr24>(w)) { fixed(Bgr24 *destPtr = &workBuffer.GetReference()) { for (int y = 0; y < h; y++) { Span <TPixel> row = imageBuffer.DangerousGetRowSpan(y); byte *sourcePtr = sourcePtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); PixelOperations <TPixel> .Instance.FromBgr24(configuration, workBuffer.GetSpan().Slice(0, w), row); } } } } finally { bmp.UnlockBits(data); } return(image); }
private static void WriteBlackAndWhite <TPixel>(Configuration configuration, Stream stream, ImageFrame <TPixel> image) where TPixel : unmanaged, IPixel <TPixel> { int width = image.Width; int height = image.Height; Buffer2D <TPixel> pixelBuffer = image.PixelBuffer; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <L8> row = allocator.Allocate <L8>(width); Span <L8> rowSpan = row.GetSpan(); int previousValue = 0; int startBit = 0; for (int y = 0; y < height; y++) { Span <TPixel> pixelSpan = pixelBuffer.DangerousGetRowSpan(y); PixelOperations <TPixel> .Instance.ToL8( configuration, pixelSpan, rowSpan); for (int x = 0; x < width;) { int value = previousValue; for (int i = startBit; i < 8; i++) { if (rowSpan[x].PackedValue < 128) { value |= 0x80 >> i; } x++; if (x == width) { previousValue = value; startBit = (i + 1) & 7; // Round off to below 8. break; } } if (startBit == 0) { stream.WriteByte((byte)value); previousValue = 0; } } } }
/// <summary> /// Reads a uncompressed TGA image where each pixels has 32 bit. /// </summary> /// <typeparam name="TPixel">The pixel type.</typeparam> /// <param name="width">The width of the image.</param> /// <param name="height">The height of the image.</param> /// <param name="pixels">The <see cref="Buffer2D{TPixel}"/> to assign the palette to.</param> /// <param name="origin">The image origin.</param> private void ReadBgra32 <TPixel>(int width, int height, Buffer2D <TPixel> pixels, TgaImageOrigin origin) where TPixel : unmanaged, IPixel <TPixel> { TPixel color = default; bool invertX = InvertX(origin); if (this.tgaMetadata.AlphaChannelBits == 8 && !invertX) { using IMemoryOwner <byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 4, 0); Span <byte> rowSpan = row.GetSpan(); if (InvertY(origin)) { for (int y = height - 1; y >= 0; y--) { this.ReadBgra32Row(width, pixels, rowSpan, y); } } else { for (int y = 0; y < height; y++) { this.ReadBgra32Row(width, pixels, rowSpan, y); } } return; } for (int y = 0; y < height; y++) { int newY = InvertY(y, height, origin); Span <TPixel> pixelRow = pixels.DangerousGetRowSpan(newY); if (invertX) { for (int x = width - 1; x >= 0; x--) { this.ReadBgra32Pixel(x, color, pixelRow); } } else { for (int x = 0; x < width; x++) { this.ReadBgra32Pixel(x, color, pixelRow); } } } }
public void LoadSpectralStride(Buffer2D <Block8x8> data, int strideIndex) { int startIndex = strideIndex * data.Height; int endIndex = Math.Min(this.HeightInBlocks, startIndex + data.Height); for (int y = startIndex; y < endIndex; y++) { Span <Block8x8> blockRow = data.DangerousGetRowSpan(y - startIndex); for (int x = 0; x < this.WidthInBlocks; x++) { this.MakeBlock(blockRow[x], y, x); } } }
public void SwapOrCopyContent_WhenDestinationIsOwned_ShouldNotSwapInDisposedSourceBuffer() { using var destData = MemoryGroup <int> .Wrap(new int[100]); using var dest = new Buffer2D <int>(destData, 10, 10); using (Buffer2D <int> source = this.MemoryAllocator.Allocate2D <int>(10, 10, AllocationOptions.Clean)) { source[0, 0] = 1; dest[0, 0] = 2; Buffer2D <int> .SwapOrCopyContent(dest, source); } int actual1 = dest.DangerousGetRowSpan(0)[0]; int actual2 = dest.DangerousGetRowSpan(0)[0]; int actual3 = dest.GetSafeRowMemory(0).Span[0]; int actual5 = dest[0, 0]; Assert.Equal(1, actual1); Assert.Equal(1, actual2); Assert.Equal(1, actual3); Assert.Equal(1, actual5); }
public Size BulkPixelConvert() { using var image = new Image <Rgba32>(800, 800); using IMemoryOwner <float> amounts = Configuration.Default.MemoryAllocator.Allocate <float>(image.Width); amounts.GetSpan().Fill(1); Buffer2D <Rgba32> pixels = image.GetRootFramePixelBuffer(); for (int y = 0; y < image.Height; y++) { Span <Rgba32> span = pixels.DangerousGetRowSpan(y); this.BulkPixelConvert(span, span, span, amounts.GetSpan()); } return(new Size(image.Width, image.Height)); }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { var intersect = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); Configuration configuration = this.Configuration; TPixel upper = this.definition.Upper.ToPixel <TPixel>(); TPixel lower = this.definition.Lower.ToPixel <TPixel>(); float thresholdLimit = this.definition.ThresholdLimit; int startY = intersect.Y; int endY = intersect.Bottom; int startX = intersect.X; int endX = intersect.Right; int width = intersect.Width; int height = intersect.Height; // ClusterSize defines the size of cluster to used to check for average. Tweaked to support up to 4k wide pixels and not more. 4096 / 16 is 256 thus the '-1' byte clusterSize = (byte)Math.Truncate((width / 16f) - 1); Buffer2D <TPixel> sourceBuffer = source.PixelBuffer; // Using pooled 2d buffer for integer image table and temp memory to hold Rgb24 converted pixel data. using (Buffer2D <ulong> intImage = this.Configuration.MemoryAllocator.Allocate2D <ulong>(width, height)) { Rgba32 rgb = default; for (int x = startX; x < endX; x++) { ulong sum = 0; for (int y = startY; y < endY; y++) { Span <TPixel> row = sourceBuffer.DangerousGetRowSpan(y); ref TPixel rowRef = ref MemoryMarshal.GetReference(row); ref TPixel color = ref Unsafe.Add(ref rowRef, x); color.ToRgba32(ref rgb); sum += (ulong)(rgb.R + rgb.G + rgb.B); if (x - startX != 0) { intImage[x - startX, y - startY] = intImage[x - startX - 1, y - startY] + sum; } else { intImage[x - startX, y - startY] = sum; } } }
/// <summary> /// Convert raw spectral DCT data to color data and copy it to the color buffer <see cref="ColorBuffer"/>. /// </summary> public void CopyBlocksToColorBuffer(int spectralStep) { Buffer2D <Block8x8> spectralBuffer = this.component.SpectralBlocks; float maximumValue = this.frame.MaxColorChannelValue; int destAreaStride = this.ColorBuffer.Width; int yBlockStart = spectralStep * this.blockRowsPerStep; Size subSamplingDivisors = this.component.SubSamplingDivisors; Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex]; Block8x8F workspaceBlock = default; for (int y = 0; y < this.blockRowsPerStep; y++) { int yBuffer = y * this.blockAreaSize.Height; Span <float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); Span <Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) { // Integer to float workspaceBlock.LoadFrom(ref blockRow[xBlock]); // Dequantize workspaceBlock.MultiplyInPlace(ref dequantTable); // Convert from spectral to color FastFloatingPointDCT.TransformIDCT(ref workspaceBlock); // To conform better to libjpeg we actually NEED TO loose precision here. // This is because they store blocks as Int16 between all the operations. // To be "more accurate", we need to emulate this by rounding! workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); // Write to color buffer acording to sampling factors int xColorBufferStart = xBlock * this.blockAreaSize.Width; workspaceBlock.ScaledCopyTo( ref colorBufferRow[xColorBufferStart], destAreaStride, subSamplingDivisors.Width, subSamplingDivisors.Height); } } }