private void TestSpanPropertyIsAlwaysTheSame <T>(int desiredLength) where T : struct { using (IMemoryOwner <T> buffer = this.MemoryAllocator.Allocate <T>(desiredLength, AllocationOptions.None)) { ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan()); ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan());
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { MemoryAllocator memoryAllocator = configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; Span <TPixel> pixels = source.GetPixelSpan(); // Build the histogram of the grayscale levels. using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) { Span <int> histogram = histogramBuffer.GetSpan(); for (int i = 0; i < pixels.Length; i++) { TPixel sourcePixel = pixels[i]; int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); histogram[luminance]++; } // Calculate the cumulative distribution function, which will map each input pixel to a new value. Span <int> cdf = cdfBuffer.GetSpan(); int cdfMin = this.CalculateCdf(cdf, histogram); // Apply the cdf to each pixel of the image float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; for (int i = 0; i < pixels.Length; i++) { TPixel sourcePixel = pixels[i]; int luminance = this.GetLuminance(sourcePixel, this.LuminanceLevels); float luminanceEqualized = cdf[luminance] / numberOfPixelsMinusCdfMin; pixels[i].FromVector4(new Vector4(luminanceEqualized)); } } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { MemoryAllocator memoryAllocator = this.Configuration.MemoryAllocator; int numberOfPixels = source.Width * source.Height; var workingRect = new Rectangle(0, 0, source.Width, source.Height); using (IMemoryOwner <int> histogramBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) using (IMemoryOwner <int> cdfBuffer = memoryAllocator.Allocate <int>(this.LuminanceLevels, AllocationOptions.Clean)) { // Build the histogram of the grayscale levels. ParallelHelper.IterateRows( workingRect, this.Configuration, rows => { ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); for (int y = rows.Min; y < rows.Max; y++) { ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); for (int x = 0; x < workingRect.Width; x++) { int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); Unsafe.Add(ref histogramBase, luminance)++; } } });
/// <inheritdoc /> internal override void Apply(Span <float> scanline, int x, int y) { MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length)) using (IMemoryOwner <TPixel> overlay = memoryAllocator.Allocate <TPixel>(scanline.Length)) { Span <float> amountSpan = amountBuffer.GetSpan(); Span <TPixel> overlaySpan = overlay.GetSpan(); for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = scanline[i] * this.Options.BlendPercentage; int offsetX = x + i; // No doubt this one can be optimized further but I can't imagine its // actually being used and can probably be removed/internalized for now overlaySpan[i] = this[offsetX, y]; } Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); } }
/// <summary> /// Bulk operation that copies the <paramref name="sourcePixels"/> to <paramref name="destinationPixels"/> in /// <typeparamref name="TSourcePixel"/> format. /// </summary> /// <typeparam name="TSourcePixel">The destination pixel type.</typeparam> /// <param name="configuration">A <see cref="Configuration"/> to configure internal operations.</param> /// <param name="sourcePixels">The <see cref="ReadOnlySpan{TSourcePixel}"/> to the source pixels.</param> /// <param name="destinationPixels">The <see cref="Span{TPixel}"/> to the destination pixels.</param> public virtual void From <TSourcePixel>( Configuration configuration, ReadOnlySpan <TSourcePixel> sourcePixels, Span <TPixel> destinationPixels) where TSourcePixel : struct, IPixel <TSourcePixel> { const int SliceLength = 1024; int numberOfSlices = sourcePixels.Length / SliceLength; using (IMemoryOwner <Vector4> tempVectors = configuration.MemoryAllocator.Allocate <Vector4>(SliceLength)) { Span <Vector4> vectorSpan = tempVectors.GetSpan(); for (int i = 0; i < numberOfSlices; i++) { int start = i * SliceLength; ReadOnlySpan <TSourcePixel> s = sourcePixels.Slice(start, SliceLength); Span <TPixel> d = destinationPixels.Slice(start, SliceLength); PixelOperations <TSourcePixel> .Instance.ToVector4(configuration, s, vectorSpan); this.FromVector4Destructive(configuration, vectorSpan, d); } int endOfCompleteSlices = numberOfSlices * SliceLength; int remainder = sourcePixels.Length - endOfCompleteSlices; if (remainder > 0) { ReadOnlySpan <TSourcePixel> s = sourcePixels.Slice(endOfCompleteSlices); Span <TPixel> d = destinationPixels.Slice(endOfCompleteSlices); vectorSpan = vectorSpan.Slice(0, remainder); PixelOperations <TSourcePixel> .Instance.ToVector4(configuration, s, vectorSpan); this.FromVector4Destructive(configuration, vectorSpan, d); } } }
/// <inheritdoc /> internal override void Apply(Span <float> scanline, int x, int y) { // Create a span for colors using (IMemoryOwner <float> amountBuffer = this.Target.MemoryAllocator.Allocate <float>(scanline.Length)) using (IMemoryOwner <TPixel> overlay = this.Target.MemoryAllocator.Allocate <TPixel>(scanline.Length)) { Span <float> amountSpan = amountBuffer.GetSpan(); Span <TPixel> overlaySpan = overlay.GetSpan(); int sourceY = (y - this.offsetY) % this.yLength; int offsetX = x - this.offsetX; Span <TPixel> sourceRow = this.sourceFrame.GetPixelRowSpan(sourceY); for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = scanline[i] * this.Options.BlendPercentage; int sourceX = (i + offsetX) % this.xLength; TPixel pixel = sourceRow[sourceX]; overlaySpan[i] = pixel; } Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend( this.sourceFrame.Configuration, destinationRow, destinationRow, overlaySpan, amountSpan); } }
public Size BulkVectorConvert() { 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.BulkVectorConvert(span, span, span, amounts.GetSpan()); } return(new Size(image.Width, image.Height)); }
/// <summary> /// Reads the gif comments. /// </summary> private void ReadComments() { int length; var stringBuilder = new StringBuilder(); while ((length = this.stream.ReadByte()) != 0) { if (length > GifConstants.MaxCommentSubBlockLength) { GifThrowHelper.ThrowInvalidImageContentException($"Gif comment length '{length}' exceeds max '{GifConstants.MaxCommentSubBlockLength}' of a comment data block"); } if (this.IgnoreMetadata) { this.stream.Seek(length, SeekOrigin.Current); continue; } using IMemoryOwner <byte> commentsBuffer = this.MemoryAllocator.Allocate <byte>(length); Span <byte> commentsSpan = commentsBuffer.GetSpan(); this.stream.Read(commentsSpan); string commentPart = GifConstants.Encoding.GetString(commentsSpan); stringBuilder.Append(commentPart); } if (stringBuilder.Length > 0) { this.gifMetadata.Comments.Add(stringBuilder.ToString()); } }
/// <summary> /// Applies the opacity weighting for each pixel in a scanline to the target based on the pattern contained in the brush. /// </summary> /// <param name="scanline">The a collection of opacity values between 0 and 1 to be merged with the brushed color value before being applied to the target.</param> /// <param name="x">The x position in the target pixel space that the start of the scanline data corresponds to.</param> /// <param name="y">The y position in the target pixel space that whole scanline corresponds to.</param> /// <remarks>scanlineBuffer will be > scanlineWidth but provide and offset in case we want to share a larger buffer across runs.</remarks> internal virtual void Apply(Span <float> scanline, int x, int y) { MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length)) using (IMemoryOwner <TPixel> overlay = memoryAllocator.Allocate <TPixel>(scanline.Length)) { Span <float> amountSpan = amountBuffer.GetSpan(); Span <TPixel> overlaySpan = overlay.GetSpan(); for (int i = 0; i < scanline.Length; i++) { if (this.Options.BlendPercentage < 1) { amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } else { amountSpan[i] = scanline[i]; } overlaySpan[i] = this[x + i, y]; } Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, overlaySpan, amountSpan); } }
/// <inheritdoc /> internal override void Apply(Span <float> scanline, int x, int y) { Span <TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length); MemoryAllocator memoryAllocator = this.Target.MemoryAllocator; if (this.Options.BlendPercentage == 1f) { this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), scanline); } else { using (IMemoryOwner <float> amountBuffer = memoryAllocator.Allocate <float>(scanline.Length)) { Span <float> amountSpan = amountBuffer.GetSpan(); for (int i = 0; i < scanline.Length; i++) { amountSpan[i] = scanline[i] * this.Options.BlendPercentage; } this.Blender.Blend(memoryAllocator, destinationRow, destinationRow, this.Colors.GetSpan(), amountSpan); } } }
protected override void EncodeStrip(int y, int height, TiffBaseCompressor compressor) { if (this.rowBuffer == null) { this.rowBuffer = this.MemoryAllocator.Allocate <byte>(this.BytesPerRow * height); } this.rowBuffer.Clear(); Span <byte> outputRowSpan = this.rowBuffer.GetSpan().Slice(0, this.BytesPerRow * height); int width = this.Image.Width; using IMemoryOwner <TPixel> stripPixelBuffer = this.MemoryAllocator.Allocate <TPixel>(height * width); Span <TPixel> stripPixels = stripPixelBuffer.GetSpan(); int lastRow = y + height; int stripPixelsRowIdx = 0; for (int row = y; row < lastRow; row++) { Span <TPixel> stripPixelsRow = this.Image.PixelBuffer.DangerousGetRowSpan(row); stripPixelsRow.CopyTo(stripPixels.Slice(stripPixelsRowIdx * width, width)); stripPixelsRowIdx++; } this.EncodePixels(stripPixels, outputRowSpan); compressor.CompressStrip(outputRowSpan, height); }
internal static unsafe Bitmap To32bppArgbSystemDrawingBitmap <TPixel>(Image <TPixel> image) where TPixel : struct, IPixel <TPixel> { int w = image.Width; int h = image.Height; var resultBitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb); var fullRect = new Rectangle(0, 0, w, h); BitmapData data = resultBitmap.LockBits(fullRect, ImageLockMode.ReadWrite, resultBitmap.PixelFormat); byte * destPtrBase = (byte *)data.Scan0; long destRowByteCount = data.Stride; long sourceRowByteCount = w * sizeof(Bgra32); using (IMemoryOwner <Bgra32> workBuffer = image.GetConfiguration().MemoryAllocator.Allocate <Bgra32>(w)) { fixed(Bgra32 *sourcePtr = &workBuffer.GetReference()) { for (int y = 0; y < h; y++) { Span <TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y); PixelOperations <TPixel> .Instance.ToBgra32(row, workBuffer.GetSpan(), row.Length); byte *destPtr = destPtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); } } } resultBitmap.UnlockBits(data); return(resultBitmap); }
public ReadOnlyMemory <TPixel> BuildPalette(ImageFrame <TPixel> source, Rectangle bounds) { 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.GetPixelRowSpan(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); } } TPixel[] palette = this.octree.Palletize(this.colors); this.pixelMap = new EuclideanPixelMap <TPixel>(palette); return(palette); }
/// <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); } } } }
/// <inheritdoc/> public override void WriteByte(byte value) { this.EnsureNotDisposed(); if (this.memoryChunk is null) { this.memoryChunk = this.AllocateMemoryChunk(); this.writeChunk = this.memoryChunk; this.writeOffset = 0; } IMemoryOwner <byte> chunkBuffer = this.writeChunk.Buffer; int chunkSize = this.writeChunk.Length; if (this.writeOffset == chunkSize) { // Allocate a new chunk if the current one is full this.writeChunk.Next = this.AllocateMemoryChunk(); this.writeChunk = this.writeChunk.Next; this.writeOffset = 0; chunkBuffer = this.writeChunk.Buffer; } chunkBuffer.GetSpan()[this.writeOffset++] = value; }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source) { TPixel vignetteColor = this.definition.VignetteColor.ToPixel <TPixel>(); float blendPercent = this.definition.GraphicsOptions.BlendPercentage; var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); Vector2 center = Rectangle.Center(interest); float finalRadiusX = this.definition.RadiusX.Calculate(interest.Size); float finalRadiusY = this.definition.RadiusY.Calculate(interest.Size); float rX = finalRadiusX > 0 ? MathF.Min(finalRadiusX, interest.Width * .5F) : interest.Width * .5F; float rY = finalRadiusY > 0 ? MathF.Min(finalRadiusY, interest.Height * .5F) : interest.Height * .5F; float maxDistance = MathF.Sqrt((rX * rX) + (rY * rY)); Configuration configuration = this.Configuration; MemoryAllocator allocator = configuration.MemoryAllocator; using IMemoryOwner <TPixel> rowColors = allocator.Allocate <TPixel>(interest.Width); rowColors.GetSpan().Fill(vignetteColor); var operation = new RowOperation(configuration, interest, rowColors, this.blender, center, maxDistance, blendPercent, source); ParallelRowIterator.IterateRows <RowOperation, float>( configuration, interest, in operation); }
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.GetRowSpan(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); } } Span <TPixel> paletteSpan = this.paletteOwner.GetSpan(); int paletteIndex = 0; this.octree.Palletize(paletteSpan, this.maxColors, ref paletteIndex); // Length of reduced palette + transparency. ReadOnlyMemory <TPixel> result = this.paletteOwner.Memory.Slice(0, Math.Min(paletteIndex + 2, QuantizerConstants.MaxColors)); this.pixelMap = new EuclideanPixelMap <TPixel>(this.Configuration, result); this.palette = result; }
/// <summary> /// Reads an individual gif frame. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The image to decode the information to.</param> /// <param name="previousFrame">The previous frame.</param> private void ReadFrame <TPixel>(ref Image <TPixel> image, ref ImageFrame <TPixel> previousFrame) where TPixel : unmanaged, IPixel <TPixel> { this.ReadImageDescriptor(); IMemoryOwner <byte> localColorTable = null; Buffer2D <byte> indices = null; try { // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. if (this.imageDescriptor.LocalColorTableFlag) { int length = this.imageDescriptor.LocalColorTableSize * 3; localColorTable = this.Configuration.MemoryAllocator.Allocate <byte>(length, AllocationOptions.Clean); this.stream.Read(localColorTable.GetSpan()); } indices = this.Configuration.MemoryAllocator.Allocate2D <byte>(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); this.ReadFrameIndices(indices); ReadOnlySpan <Rgb24> colorTable = MemoryMarshal.Cast <byte, Rgb24>((localColorTable ?? this.globalColorTable).GetSpan()); this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); // Skip any remaining blocks this.SkipBlock(); } finally { localColorTable?.Dispose(); indices?.Dispose(); } }
private void TestSpanPropertyIsAlwaysTheSame <T>(int desiredLength, bool testManagedByteBuffer = false) where T : struct { using (IMemoryOwner <T> buffer = this.Allocate <T>(desiredLength, AllocationOptions.None, testManagedByteBuffer)) { ref T a = ref MemoryMarshal.GetReference(buffer.GetSpan()); ref T b = ref MemoryMarshal.GetReference(buffer.GetSpan());
/// <summary> /// Initializes a new instance of the <see cref="HuffmanTable"/> struct. /// </summary> /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param> /// <param name="codeLengths">The code lengths</param> /// <param name="values">The huffman values</param> public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan <byte> codeLengths, ReadOnlySpan <byte> values) { const int Length = 257; using (IMemoryOwner <short> huffcode = memoryAllocator.Allocate <short>(Length)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
private void TestHasCorrectLength <T>(int desiredLength) where T : struct { using (IMemoryOwner <T> buffer = this.MemoryAllocator.Allocate <T>(desiredLength)) { Assert.Equal(desiredLength, buffer.GetSpan().Length); } }
/// <summary> /// Reads a run length encoded TGA image. /// </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="bytesPerPixel">The bytes per pixel.</param> /// <param name="inverted">Indicates, if the origin of the image is top left rather the bottom left (the default).</param> private void ReadRle <TPixel>(int width, int height, Buffer2D <TPixel> pixels, int bytesPerPixel, bool inverted) where TPixel : unmanaged, IPixel <TPixel> { TPixel color = default; var alphaBits = this.tgaMetadata.AlphaChannelBits; using (IMemoryOwner <byte> buffer = this.memoryAllocator.Allocate <byte>(width * height * bytesPerPixel, AllocationOptions.Clean)) { Span <byte> bufferSpan = buffer.GetSpan(); this.UncompressRle(width, height, bufferSpan, bytesPerPixel); for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); Span <TPixel> pixelRow = pixels.GetRowSpan(newY); int rowStartIdx = y * width * bytesPerPixel; for (int x = 0; x < width; x++) { int idx = rowStartIdx + (x * bytesPerPixel); switch (bytesPerPixel) { case 1: color.FromL8(Unsafe.As <byte, L8>(ref bufferSpan[idx])); break; case 2: if (!this.hasAlpha) { // Set alpha value to 1, to treat it as opaque for Bgra5551. bufferSpan[idx + 1] = (byte)(bufferSpan[idx + 1] | 128); } color.FromBgra5551(Unsafe.As <byte, Bgra5551>(ref bufferSpan[idx])); break; case 3: color.FromBgr24(Unsafe.As <byte, Bgr24>(ref bufferSpan[idx])); break; case 4: if (this.hasAlpha) { color.FromBgra32(Unsafe.As <byte, Bgra32>(ref bufferSpan[idx])); } else { var alpha = alphaBits == 0 ? byte.MaxValue : bufferSpan[idx + 3]; color.FromBgra32(new Bgra32(bufferSpan[idx + 2], bufferSpan[idx + 1], bufferSpan[idx], (byte)alpha)); } break; } pixelRow[x] = color; } } } }
/// <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; }
/// <summary> /// Initializes a new instance of the <see cref="HuffmanTable"/> struct. /// </summary> /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations.</param> /// <param name="codeLengths">The code lengths</param> /// <param name="values">The huffman values</param> public HuffmanTable(MemoryAllocator memoryAllocator, ReadOnlySpan <byte> codeLengths, ReadOnlySpan <byte> values) { // We do some bounds checks in the code here to protect against AccessViolationExceptions const int HuffCodeLength = 257; const int MaxSizeLength = HuffCodeLength - 1; using (IMemoryOwner <short> huffcode = memoryAllocator.Allocate <short>(HuffCodeLength)) { ref short huffcodeRef = ref MemoryMarshal.GetReference(huffcode.GetSpan()); ref byte codeLengthsRef = ref MemoryMarshal.GetReference(codeLengths);
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { 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; } int width = maxX - minX; using (IMemoryOwner <TPixel> colors = source.MemoryAllocator.Allocate <TPixel>(width)) using (IMemoryOwner <float> amount = source.MemoryAllocator.Allocate <float>(width)) { // Be careful! Do not capture colorSpan & amountSpan in the lambda below! Span <TPixel> colorSpan = colors.GetSpan(); Span <float> amountSpan = amount.GetSpan(); // TODO: Use Span.Fill? for (int i = 0; i < width; i++) { colorSpan[i] = this.Color; amountSpan[i] = this.GraphicsOptions.BlendPercentage; } PixelBlender <TPixel> blender = PixelOperations <TPixel> .Instance.GetPixelBlender(this.GraphicsOptions); ParallelFor.WithConfiguration( minY, maxY, configuration, y => { Span <TPixel> destination = source.GetPixelRowSpan(y - startY).Slice(minX - startX, width); // This switched color & destination in the 2nd and 3rd places because we are applying the target color under the current one blender.Blend(source.MemoryAllocator, destination, colors.GetSpan(), destination, amount.GetSpan()); }); } }
/// <inheritdoc/> protected override void OnFrameApply(ImageFrame <TPixel> source, Rectangle sourceRectangle, Configuration configuration) { 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); float maxDistance = this.Radius > 0 ? Math.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 (IMemoryOwner <TPixel> rowColors = Configuration.Default.MemoryAllocator.Allocate <TPixel>(width)) { Buffer2D <TPixel> sourcePixels = source.PixelBuffer; rowColors.GetSpan().Fill(glowColor); Parallel.For( minY, maxY, configuration.ParallelOptions, y => { int offsetY = y - startY; for (int x = minX; x < maxX; x++) { int offsetX = x - startX; float distance = Vector2.Distance(centre, new Vector2(offsetX, offsetY)); Vector4 sourceColor = sourcePixels[offsetX, offsetY].ToVector4(); TPixel packed = default(TPixel); packed.PackFromVector4(PremultipliedLerp(sourceColor, glowColor.ToVector4(), 1 - (.95F * (distance / maxDistance)))); sourcePixels[offsetX, offsetY] = packed; } }); } }
/// <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 the transformations, if any are present. /// </summary> /// <param name="xSize">The width of the image.</param> /// <param name="ySize">The height of the image.</param> /// <param name="decoder">Vp8LDecoder where the transformations will be stored.</param> private void ReadTransformation(int xSize, int ySize, Vp8LDecoder decoder) { var transformType = (Vp8LTransformType)this.bitReader.ReadValue(2); var transform = new Vp8LTransform(transformType, xSize, ySize); // Each transform is allowed to be used only once. foreach (Vp8LTransform decoderTransform in decoder.Transforms) { if (decoderTransform.TransformType == transform.TransformType) { WebpThrowHelper.ThrowImageFormatException("Each transform can only be present once"); } } switch (transformType) { case Vp8LTransformType.SubtractGreen: // There is no data associated with this transform. break; case Vp8LTransformType.ColorIndexingTransform: // The transform data contains color table size and the entries in the color table. // 8 bit value for color table size. uint numColors = this.bitReader.ReadValue(8) + 1; int bits = numColors > 16 ? 0 : numColors > 4 ? 1 : numColors > 2 ? 2 : 3; transform.Bits = bits; using (IMemoryOwner <uint> colorMap = this.DecodeImageStream(decoder, (int)numColors, 1, false)) { int finalNumColors = 1 << (8 >> transform.Bits); IMemoryOwner <uint> newColorMap = this.memoryAllocator.Allocate <uint>(finalNumColors, AllocationOptions.Clean); LosslessUtils.ExpandColorMap((int)numColors, colorMap.GetSpan(), newColorMap.GetSpan()); transform.Data = newColorMap; } break; case Vp8LTransformType.PredictorTransform: case Vp8LTransformType.CrossColorTransform: { // The first 3 bits of prediction data define the block width and height in number of bits. transform.Bits = (int)this.bitReader.ReadValue(3) + 2; int blockWidth = LosslessUtils.SubSampleSize(transform.XSize, transform.Bits); int blockHeight = LosslessUtils.SubSampleSize(transform.YSize, transform.Bits); IMemoryOwner <uint> transformData = this.DecodeImageStream(decoder, blockWidth, blockHeight, false); transform.Data = transformData; break; } } decoder.Transforms.Add(transform); }
/// <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.Format32bppArgb"/></exception> internal static unsafe Image <TPixel> From32bppArgbSystemDrawingBitmap <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.Format32bppArgb) { throw new ArgumentException( $"{nameof(From32bppArgbSystemDrawingBitmap)} : pixel format should be {PixelFormat.Format32bppArgb}!", 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(Bgra32); Configuration configuration = image.GetConfiguration(); using (IMemoryOwner <Bgra32> workBuffer = Configuration.Default.MemoryAllocator.Allocate <Bgra32>(w)) { fixed(Bgra32 *destPtr = &workBuffer.GetReference()) { for (int y = 0; y < h; y++) { Span <TPixel> row = image.Frames.RootFrame.GetPixelRowSpan(y); byte *sourcePtr = sourcePtrBase + (data.Stride * y); Buffer.MemoryCopy(sourcePtr, destPtr, destRowByteCount, sourceRowByteCount); PixelOperations <TPixel> .Instance.FromBgra32( configuration, workBuffer.GetSpan().Slice(0, w), row); } } } } finally { bmp.UnlockBits(data); } return(image); }
/// <summary> /// Write remainder of this stream to another stream. /// </summary> /// <param name="stream">The stream to write to.</param> public void WriteTo(Stream stream) { this.EnsureNotDisposed(); Guard.NotNull(stream, nameof(stream)); if (this.readChunk is null) { if (this.memoryChunk is null) { return; } this.readChunk = this.memoryChunk; this.readOffset = 0; } IMemoryOwner <byte> chunkBuffer = this.readChunk.Buffer; int chunkSize = this.readChunk.Length; if (this.readChunk.Next is null) { chunkSize = this.writeOffset; } // Following code mirrors Read() logic (readChunk/readOffset should // point just past last byte of last chunk when done) // loop until end of chunks is found while (true) { if (this.readOffset == chunkSize) { // Exit if no more chunks are currently available if (this.readChunk.Next is null) { break; } this.readChunk = this.readChunk.Next; this.readOffset = 0; chunkBuffer = this.readChunk.Buffer; chunkSize = this.readChunk.Length; if (this.readChunk.Next is null) { chunkSize = this.writeOffset; } } int writeCount = chunkSize - this.readOffset; stream.Write(chunkBuffer.GetSpan(), this.readOffset, writeCount); this.readOffset = chunkSize; } }