// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageSharp.Formats.Jpg { using System.Buffers; using System.Runtime.CompilerServices; /// /// Represents an area of a Jpeg subimage (channel) /// internal struct JpegPixelArea { /// /// Initializes a new instance of the struct from existing data. /// /// The pixel array /// The stride /// The offset public JpegPixelArea(byte[] pixels, int striede, int offset) { this.Stride = striede; this.Pixels = pixels; this.Offset = offset; } /// /// Gets the pixels. /// public byte[] Pixels { get; private set; } /// /// Gets a value indicating whether the instance has been initalized. (Is not default(JpegPixelArea)) /// public bool IsInitialized => this.Pixels != null; /// /// Gets or the stride. /// public int Stride { get; } /// /// Gets or the offset. /// public int Offset { get; } /// /// Gets a of bytes to the pixel area /// public MutableSpan Span => new MutableSpan(this.Pixels, this.Offset); private static ArrayPool BytePool => ArrayPool.Shared; /// /// Returns the pixel at (x, y) /// /// The x index /// The y index /// The pixel value public byte this[int x, int y] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return this.Pixels[(y * this.Stride) + x]; } } /// /// Creates a new instance of the struct. /// Pixel array will be taken from a pool, this instance will be the owner of it's pixel data, therefore /// should be called when the instance is no longer needed. /// /// The width. /// The height. /// A with pooled data public static JpegPixelArea CreatePooled(int width, int height) { int size = width * height; var pixels = BytePool.Rent(size); return new JpegPixelArea(pixels, width, 0); } /// /// Returns to the pool /// public void ReturnPooled() { if (this.Pixels == null) { return; } BytePool.Return(this.Pixels); this.Pixels = null; } /// /// Gets the subarea that belongs to the Block8x8 defined by block indices /// /// The block X index /// The block Y index /// The subarea offseted by block indices public JpegPixelArea GetOffsetedSubAreaForBlock(int bx, int by) { int offset = this.Offset + (8 * ((by * this.Stride) + bx)); return new JpegPixelArea(this.Pixels, this.Stride, offset); } /// /// Gets the row offset at the given position /// /// The y-coordinate of the image. /// The public int GetRowOffset(int y) { return this.Offset + (y * this.Stride); } /// /// Load values to the pixel area from the given . /// Level shift [-128.0, 128.0] floating point color values by +128, clip them to [0, 255], and convert them to /// values /// /// The block holding the color values /// Temporal block provided by the caller [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void LoadColorsFrom(Block8x8F* block, Block8x8F* temp) { // Level shift by +128, clip to [0, 255], and write to dst. block->CopyColorsTo(new MutableSpan(this.Pixels, this.Offset), this.Stride, temp); } } }