public AvifWriterState(IReadOnlyList <CompressedAV1Image> colorImages, IReadOnlyList <CompressedAV1Image> alphaImages, ImageGridMetadata imageGridMetadata, AvifMetadata metadata, IByteArrayPool arrayPool) { if (colorImages is null) { ExceptionUtil.ThrowArgumentNullException(nameof(colorImages)); } if (metadata is null) { ExceptionUtil.ThrowArgumentNullException(nameof(metadata)); } if (arrayPool is null) { ExceptionUtil.ThrowArgumentNullException(nameof(arrayPool)); } this.ImageGrid = imageGridMetadata; this.items = new List <AvifWriterItem>(GetItemCount(colorImages, alphaImages, metadata)); Initialize(colorImages, alphaImages, imageGridMetadata, metadata, arrayPool); }
/// <summary> /// Reads the specified number of bytes from the stream, starting from a specified point in the byte array. /// </summary> /// <param name="bytes">The bytes.</param> /// <param name="offset">The starting offset in the array.</param> /// <param name="count">The count.</param> /// <exception cref="ArgumentNullException"><paramref name="bytes"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> is negative.</exception> /// <exception cref="EndOfStreamException">The end of the stream has been reached.</exception> /// <exception cref="ObjectDisposedException">The object has been disposed.</exception> public unsafe void ProperRead(byte[] bytes, int offset, int count) { if (bytes is null) { ExceptionUtil.ThrowArgumentNullException(nameof(bytes)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } VerifyNotDisposed(); if (count == 0) { return; } int totalBytesRead = 0; while (totalBytesRead < count) { int bytesRead = ReadInternal(bytes, offset + totalBytesRead, count - totalBytesRead); if (bytesRead == 0) { throw new EndOfStreamException(); } totalBytesRead += bytesRead; } }
/// <summary> /// Initializes a new instance of the <see cref="AvifReader"/> class. /// </summary> /// <param name="parser">The parser.</param> /// <exception cref="ArgumentNullException"><paramref name="input"/> is null.</exception> public AvifReader(Stream input, bool leaveOpen, IByteArrayPool arrayPool) { if (input is null) { ExceptionUtil.ThrowArgumentNullException(nameof(input)); } // The parser is initialized first because it will throw an exception // if the AVIF file is invalid or not supported. this.parser = new AvifParser(input, leaveOpen, arrayPool); this.primaryItemId = this.parser.GetPrimaryItemId(); this.alphaItemId = this.parser.GetAlphaItemId(this.primaryItemId); this.colorInfoBox = this.parser.TryGetColorInfoBox(this.primaryItemId); this.parser.GetTransformationProperties(this.primaryItemId, out this.cleanApertureBox, out this.imageRotateBox, out this.imageMirrorBox); this.colorGridInfo = this.parser.TryGetImageGridInfo(this.primaryItemId); if (this.alphaItemId != 0) { this.alphaGridInfo = this.parser.TryGetImageGridInfo(this.alphaItemId); } else { this.alphaGridInfo = null; } }
public static void DecompressAlpha(AvifItemData alphaImage, DecodeInfo decodeInfo, Surface fullSurface) { if (alphaImage is null) { ExceptionUtil.ThrowArgumentNullException(nameof(alphaImage)); } if (decodeInfo is null) { ExceptionUtil.ThrowArgumentNullException(nameof(decodeInfo)); } if (fullSurface is null) { ExceptionUtil.ThrowArgumentNullException(nameof(fullSurface)); } DecoderStatus status = DecoderStatus.Ok; unsafe { alphaImage.UseBufferPointer((ptr, length) => { BitmapData bitmapData = new BitmapData { scan0 = fullSurface.Scan0.Pointer, width = (uint)fullSurface.Width, height = (uint)fullSurface.Height, stride = (uint)fullSurface.Stride }; UIntPtr alphaImageSize = new UIntPtr(length); if (IntPtr.Size == 8) { status = AvifNative_64.DecompressAlphaImage(ptr, alphaImageSize, decodeInfo, ref bitmapData); } else { status = AvifNative_86.DecompressAlphaImage(ptr, alphaImageSize, decodeInfo, ref bitmapData); } }); } if (status != DecoderStatus.Ok) { HandleError(status); } }
public void Write(byte[] bytes) { if (bytes is null) { ExceptionUtil.ThrowArgumentNullException(nameof(bytes)); } Write(bytes, 0, bytes.Length); }
public unsafe void Write(SafeBuffer buffer) { if (buffer is null) { ExceptionUtil.ThrowArgumentNullException(nameof(buffer)); } VerifyNotDisposed(); ulong length = buffer.ByteLength; if (length == 0) { return; } // The largest multiple of 4096 that is under the large object heap limit. const int MaxBufferSize = 81920; int bufferSize = (int)Math.Min(length, MaxBufferSize); byte[] writeBuffer = this.arrayPool.Rent(bufferSize); byte *readPtr = null; System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref readPtr); fixed(byte *writePtr = writeBuffer) { ulong totalBytesRead = 0; while (totalBytesRead < length) { ulong bytesRead = Math.Min(length - totalBytesRead, MaxBufferSize); Buffer.MemoryCopy(readPtr + totalBytesRead, writePtr, bytesRead, bytesRead); this.stream.Write(writeBuffer, 0, (int)bytesRead); totalBytesRead += bytesRead; } } } finally { if (readPtr != null) { buffer.ReleasePointer(); } } this.arrayPool.Return(writeBuffer); }
#pragma warning restore IDE0032 // Use auto property public EndianBinaryReaderSegment(EndianBinaryReader reader, long startOffset, long length) { if (reader is null) { ExceptionUtil.ThrowArgumentNullException(nameof(reader)); } this.reader = reader; this.startOffset = startOffset; this.endOffset = checked (startOffset + length); }
public unsafe void UseBufferPointer(UseBufferPointerDelegate action) { if (action is null) { ExceptionUtil.ThrowArgumentNullException(nameof(action)); } VerifyNotDisposed(); UseBufferPointerImpl(action); }
/// <summary> /// Reads the specified number of bytes from the stream, starting from a specified point in the SafeBuffer. /// </summary> /// <param name="buffer">The buffer.</param> /// <param name="offset">The starting offset in the buffer.</param> /// <param name="count">The count.</param> /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception> /// <exception cref="EndOfStreamException">The end of the stream has been reached.</exception> /// <exception cref="ObjectDisposedException">The object has been disposed.</exception> public unsafe void ProperRead(SafeBuffer buffer, ulong offset, ulong count) { if (buffer is null) { ExceptionUtil.ThrowArgumentNullException(nameof(buffer)); } VerifyNotDisposed(); if (count == 0) { return; } // The largest multiple of 4096 that is under the large object heap limit. const int MaxReadBufferSize = 81920; byte[] readBuffer = this.arrayPool.Rent((int)Math.Min(count, MaxReadBufferSize)); byte *writePtr = null; System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); try { buffer.AcquirePointer(ref writePtr); fixed(byte *readPtr = readBuffer) { ulong totalBytesRead = 0; while (totalBytesRead < count) { ulong bytesRead = (ulong)ReadInternal(readBuffer, 0, (int)Math.Min(count - totalBytesRead, MaxReadBufferSize)); if (bytesRead == 0) { throw new EndOfStreamException(); } Buffer.MemoryCopy(readPtr, writePtr + offset + totalBytesRead, bytesRead, bytesRead); totalBytesRead += bytesRead; } } } finally { if (writePtr != null) { buffer.ReleasePointer(); } } this.arrayPool.Return(readBuffer); }
public BigEndianBinaryWriter(Stream stream, bool leaveOpen, IByteArrayPool arrayPool) { if (stream is null) { ExceptionUtil.ThrowArgumentNullException(nameof(stream)); } this.stream = stream; this.leaveOpen = leaveOpen; this.buffer = new byte[sizeof(ulong)]; this.arrayPool = arrayPool; }
public AvifParser(Stream stream, bool leaveOpen, IByteArrayPool arrayPool) { if (stream is null) { ExceptionUtil.ThrowArgumentNullException(nameof(stream)); } this.arrayPool = arrayPool; this.reader = new EndianBinaryReader(stream, Endianess.Big, leaveOpen, arrayPool); Parse(); this.fileLength = (ulong)stream.Length; }
public AvifItemData ReadItemData(ItemLocationEntry entry) { if (entry is null) { ExceptionUtil.ThrowArgumentNullException(nameof(entry)); } AvifItemData data; if (entry.Extents.Count == 1) { long offset = CalculateExtentOffset(entry.BaseOffset, entry.ConstructionMethod, entry.Extents[0]); this.reader.Position = offset; ulong totalItemSize = entry.TotalItemSize; if (totalItemSize <= ManagedAvifItemDataMaxSize) { ManagedAvifItemData managedItemData = new ManagedAvifItemData((int)totalItemSize, this.arrayPool); this.reader.ProperRead(managedItemData.GetBuffer(), 0, (int)managedItemData.Length); data = managedItemData; } else { UnmanagedAvifItemData unmanagedItemData = new UnmanagedAvifItemData(totalItemSize); try { this.reader.ProperRead(unmanagedItemData.UnmanagedBuffer, 0, totalItemSize); data = unmanagedItemData; unmanagedItemData = null; } finally { unmanagedItemData?.Dispose(); } } } else { data = ReadDataFromMultipleExtents(entry); } return(data); }
private AvifWriterItem(uint id, string name, ulong dataBoxOffset, ulong length) { if (name is null) { ExceptionUtil.ThrowArgumentNullException(nameof(name)); } this.Id = id; this.Name = name; this.Image = null; this.IsAlphaImage = false; this.ContentBytes = null; this.ItemInfoEntry = new ImageGridItemInfoEntryBox(id, name); this.ItemLocation = new ItemLocationEntry(id, dataBoxOffset, length); this.ItemReferences = new List <ItemReferenceEntryBox>(); }
private AvifWriterItem(uint id, string name, CompressedAV1Image image, bool isAlphaImage) { if (image is null) { ExceptionUtil.ThrowArgumentNullException(nameof(image)); } this.Id = id; this.Name = name; this.Image = image; this.IsAlphaImage = isAlphaImage; this.ContentBytes = null; this.ItemInfoEntry = new AV01ItemInfoEntryBox(id, name); this.ItemLocation = new ItemLocationEntry(id, image.Data.ByteLength); this.ItemReferences = new List <ItemReferenceEntryBox>(); }
internal static void Crop(CleanApertureBox cleanApertureBox, ref Surface surface) { if (cleanApertureBox is null) { ExceptionUtil.ThrowArgumentNullException(nameof(cleanApertureBox)); } if (cleanApertureBox.Width.Denominator == 0 || cleanApertureBox.Height.Denominator == 0 || cleanApertureBox.HorizontalOffset.Denominator == 0 || cleanApertureBox.VerticalOffset.Denominator == 0) { return; } int cropWidth = cleanApertureBox.Width.ToInt32(); int cropHeight = cleanApertureBox.Height.ToInt32(); double offsetX = cleanApertureBox.HorizontalOffset.ToDouble(); double offsetY = cleanApertureBox.VerticalOffset.ToDouble(); double pictureCenterX = offsetX + ((surface.Width - 1) / 2.0); double pictureCenterY = offsetY + ((surface.Height - 1) / 2.0); int cropRectX = (int)Math.Round(pictureCenterX - ((cropWidth - 1) / 2.0)); int cropRectY = (int)Math.Round(pictureCenterY - ((cropHeight - 1) / 2.0)); Rectangle cropRect = new Rectangle(cropRectX, cropRectY, cropWidth, cropHeight); // Check that the crop rectangle is within the surface bounds. if (cropRect.IntersectsWith(surface.Bounds)) { Surface temp = new Surface(cropWidth, cropHeight); try { temp.CopySurface(surface, cropRect); surface.Dispose(); surface = temp; temp = null; } finally { temp?.Dispose(); } } }
/// <summary> /// Initializes a new instance of the <see cref="EndianBinaryReader"/> class. /// </summary> /// <param name="stream">The stream.</param> /// <param name="byteOrder">The byte order of the stream.</param> /// <param name="leaveOpen"> /// <see langword="true"/> to leave the stream open after the EndianBinaryReader is disposed; otherwise, <see langword="false"/> /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="stream"/> is null. /// /// -or- /// /// <paramref name="arrayPool"/> is null. /// </exception> public EndianBinaryReader(Stream stream, Endianess byteOrder, bool leaveOpen, IByteArrayPool arrayPool) { if (arrayPool is null) { ExceptionUtil.ThrowArgumentNullException(nameof(arrayPool)); } this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); this.bufferSize = (int)Math.Min(stream.Length, MaxBufferSize); this.buffer = arrayPool.Rent(this.bufferSize); this.endianess = byteOrder; this.leaveOpen = leaveOpen; this.arrayPool = arrayPool; this.readOffset = 0; this.readLength = 0; this.disposed = false; }
private AvifWriterItem(uint id, string name, byte[] contentBytes, ItemInfoEntryBox itemInfo) { if (contentBytes is null) { ExceptionUtil.ThrowArgumentNullException(nameof(contentBytes)); } if (itemInfo is null) { ExceptionUtil.ThrowArgumentNullException(nameof(itemInfo)); } this.Id = id; this.Name = name; this.Image = null; this.IsAlphaImage = false; this.ContentBytes = contentBytes; this.ItemInfoEntry = itemInfo; this.ItemLocation = new ItemLocationEntry(id, (ulong)contentBytes.Length); this.ItemReferences = new List <ItemReferenceEntryBox>(); }
public static void DecompressColor(AvifItemData colorImage, CICPColorData?colorConversionInfo, DecodeInfo decodeInfo, Surface fullSurface) { if (colorImage is null) { ExceptionUtil.ThrowArgumentNullException(nameof(colorImage)); } if (decodeInfo is null) { ExceptionUtil.ThrowArgumentNullException(nameof(decodeInfo)); } if (fullSurface is null) { ExceptionUtil.ThrowArgumentNullException(nameof(fullSurface)); } DecoderStatus status = DecoderStatus.Ok; unsafe { colorImage.UseBufferPointer((ptr, length) => { BitmapData bitmapData = new BitmapData { scan0 = fullSurface.Scan0.Pointer, width = (uint)fullSurface.Width, height = (uint)fullSurface.Height, stride = (uint)fullSurface.Stride }; UIntPtr colorImageSize = new UIntPtr(length); if (colorConversionInfo.HasValue) { CICPColorData colorData = colorConversionInfo.Value; if (IntPtr.Size == 8) { status = AvifNative_64.DecompressColorImage(ptr, colorImageSize, ref colorData, decodeInfo, ref bitmapData); } else { status = AvifNative_86.DecompressColorImage(ptr, colorImageSize, ref colorData, decodeInfo, ref bitmapData); } } else { if (IntPtr.Size == 8) { status = AvifNative_64.DecompressColorImage(ptr, colorImageSize, IntPtr.Zero, decodeInfo, ref bitmapData); } else { status = AvifNative_86.DecompressColorImage(ptr, colorImageSize, IntPtr.Zero, decodeInfo, ref bitmapData); } } }); } if (status != DecoderStatus.Ok) { HandleError(status); } }
private long CalculateExtentOffset(ulong baseOffset, ConstructionMethod constructionMethod, ItemLocationExtent extent) { if (extent is null) { ExceptionUtil.ThrowArgumentNullException(nameof(extent)); } ulong offset; try { if (constructionMethod == ConstructionMethod.FileOffset) { checked { offset = baseOffset + extent.Offset; if ((offset + extent.Length) > this.fileLength) { throw new FormatException("The item has an invalid file offset."); } } } else if (constructionMethod == ConstructionMethod.IDatBoxOffset) { ItemDataBox dataBox = this.metaBox.ItemData; if (dataBox is null) { throw new FormatException("The file does not have an item data box."); } checked { if ((extent.Offset + extent.Length) > (ulong)dataBox.Length) { throw new FormatException("The item has an invalid data box offset."); } offset = (ulong)dataBox.Offset + extent.Offset; if ((offset + extent.Length) > this.fileLength) { throw new FormatException("The item has an invalid file offset."); } } } else { throw new FormatException($"ItemLocationEntry construction method { constructionMethod } is not supported."); } } catch (OverflowException ex) { throw new FormatException("Overflow when attempting to calculate the item file offset.", ex); } if (offset > long.MaxValue) { throw new FormatException($"The item file offset exceeds {long.MaxValue:F0} bytes."); } return((long)offset); }