Browse Source

(Vp8) Write total size after writing. Separate the writes of each block

pull/2569/head
Poker 3 years ago
parent
commit
95d36af396
No known key found for this signature in database GPG Key ID: C65A6AD457D5C8F8
  1. 47
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  2. 135
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  3. 10
      src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs
  4. 8
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  5. 27
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  6. 11
      src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs
  7. 4
      src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs

47
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -4,6 +4,7 @@
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
namespace SixLabors.ImageSharp.Formats.Webp.BitWriter; namespace SixLabors.ImageSharp.Formats.Webp.BitWriter;
@ -41,17 +42,23 @@ internal abstract class BitWriterBase
public byte[] Buffer => this.buffer; public byte[] Buffer => this.buffer;
/// <summary>
/// Gets the number of bytes of the encoded image data.
/// </summary>
/// <returns>The number of bytes of the image data.</returns>
public abstract int NumBytes { get; }
/// <summary> /// <summary>
/// Writes the encoded bytes of the image to the stream. Call Finish() before this. /// Writes the encoded bytes of the image to the stream. Call Finish() before this.
/// </summary> /// </summary>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
public void WriteToStream(Stream stream) => stream.Write(this.Buffer.AsSpan(0, this.NumBytes())); public void WriteToStream(Stream stream) => stream.Write(this.Buffer.AsSpan(0, this.NumBytes));
/// <summary> /// <summary>
/// Writes the encoded bytes of the image to the given buffer. Call Finish() before this. /// Writes the encoded bytes of the image to the given buffer. Call Finish() before this.
/// </summary> /// </summary>
/// <param name="dest">The destination buffer.</param> /// <param name="dest">The destination buffer.</param>
public void WriteToBuffer(Span<byte> dest) => this.Buffer.AsSpan(0, this.NumBytes()).CopyTo(dest); public void WriteToBuffer(Span<byte> dest) => this.Buffer.AsSpan(0, this.NumBytes).CopyTo(dest);
/// <summary> /// <summary>
/// Resizes the buffer to write to. /// Resizes the buffer to write to.
@ -59,12 +66,6 @@ internal abstract class BitWriterBase
/// <param name="extraSize">The extra size in bytes needed.</param> /// <param name="extraSize">The extra size in bytes needed.</param>
public abstract void BitWriterResize(int extraSize); public abstract void BitWriterResize(int extraSize);
/// <summary>
/// Returns the number of bytes of the encoded image data.
/// </summary>
/// <returns>The number of bytes of the image data.</returns>
public abstract int NumBytes();
/// <summary> /// <summary>
/// Flush leftover bits. /// Flush leftover bits.
/// </summary> /// </summary>
@ -86,6 +87,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes the RIFF header to the stream. /// Writes the RIFF header to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="riffSize">The block length.</param> /// <param name="riffSize">The block length.</param>
protected void WriteRiffHeader(Stream stream, uint riffSize) protected void WriteRiffHeader(Stream stream, uint riffSize)
@ -99,6 +101,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Calculates the chunk size of EXIF, XMP or ICCP metadata. /// Calculates the chunk size of EXIF, XMP or ICCP metadata.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="metadataBytes">The metadata profile bytes.</param> /// <param name="metadataBytes">The metadata profile bytes.</param>
/// <returns>The metadata chunk size in bytes.</returns> /// <returns>The metadata chunk size in bytes.</returns>
protected static uint MetadataChunkSize(byte[] metadataBytes) protected static uint MetadataChunkSize(byte[] metadataBytes)
@ -118,9 +121,26 @@ internal abstract class BitWriterBase
return WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1); return WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
} }
/// <summary>
/// Overwrites ides the write file size.
/// </summary>
/// <param name="stream">The stream to write to.</param>
protected static void OverwriteFileSize(Stream stream)
{
uint position = (uint)stream.Position;
stream.Position = 4;
byte[] buffer = new byte[4];
// "RIFF"(4)+uint32 size(4)
BinaryPrimitives.WriteUInt32LittleEndian(buffer, position - WebpConstants.ChunkHeaderSize);
stream.Write(buffer);
stream.Position = position;
}
/// <summary> /// <summary>
/// Writes a metadata profile (EXIF or XMP) to the stream. /// Writes a metadata profile (EXIF or XMP) to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="metadataBytes">The metadata profile's bytes.</param> /// <param name="metadataBytes">The metadata profile's bytes.</param>
/// <param name="chunkType">The chuck type to write.</param> /// <param name="chunkType">The chuck type to write.</param>
@ -146,6 +166,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes the color profile(<see cref="WebpChunkType.Iccp"/>) to the stream. /// Writes the color profile(<see cref="WebpChunkType.Iccp"/>) to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="iccProfileBytes">The color profile bytes.</param> /// <param name="iccProfileBytes">The color profile bytes.</param>
protected void WriteColorProfile(Stream stream, byte[] iccProfileBytes) => this.WriteMetadataProfile(stream, iccProfileBytes, WebpChunkType.Iccp); protected void WriteColorProfile(Stream stream, byte[] iccProfileBytes) => this.WriteMetadataProfile(stream, iccProfileBytes, WebpChunkType.Iccp);
@ -153,6 +174,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes the animation parameter(<see cref="WebpChunkType.AnimationParameter"/>) to the stream. /// Writes the animation parameter(<see cref="WebpChunkType.AnimationParameter"/>) to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="background"> /// <param name="background">
/// The default background color of the canvas in [Blue, Green, Red, Alpha] byte order. /// The default background color of the canvas in [Blue, Green, Red, Alpha] byte order.
@ -177,6 +199,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes the animation frame(<see cref="WebpChunkType.Animation"/>) to the stream. /// Writes the animation frame(<see cref="WebpChunkType.Animation"/>) to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="animation">Animation frame data.</param> /// <param name="animation">Animation frame data.</param>
/// <param name="data">Frame data.</param> /// <param name="data">Frame data.</param>
@ -201,6 +224,7 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes the alpha chunk to the stream. /// Writes the alpha chunk to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="dataBytes">The alpha channel data bytes.</param> /// <param name="dataBytes">The alpha channel data bytes.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param> /// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param>
@ -232,14 +256,15 @@ internal abstract class BitWriterBase
/// <summary> /// <summary>
/// Writes a VP8X header to the stream. /// Writes a VP8X header to the stream.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="exifProfile">A exif profile or null, if it does not exist.</param> /// <param name="exifProfile">A exif profile or null, if it does not exist.</param>
/// <param name="xmpProfile">A XMP profile or null, if it does not exist.</param> /// <param name="xmpProfile">A XMP profile or null, if it does not exist.</param>
/// <param name="iccProfileBytes">The color profile bytes.</param> /// <param name="iccProfile">The color profile.</param>
/// <param name="width">The width of the image.</param> /// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param> /// <param name="height">The height of the image.</param>
/// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param> /// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
protected void WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, byte[]? iccProfileBytes, uint width, uint height, bool hasAlpha) protected void WriteVp8XHeader(Stream stream, ExifProfile? exifProfile, XmpProfile? xmpProfile, IccProfile? iccProfile, uint width, uint height, bool hasAlpha)
{ {
if (width > MaxDimension || height > MaxDimension) if (width > MaxDimension || height > MaxDimension)
{ {
@ -279,7 +304,7 @@ internal abstract class BitWriterBase
flags |= 16; flags |= 16;
} }
if (iccProfileBytes != null) if (iccProfile != null)
{ {
// Set iccp flag. // Set iccp flag.
flags |= 32; flags |= 32;

135
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -72,7 +72,7 @@ internal class Vp8BitWriter : BitWriterBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override int NumBytes() => (int)this.pos; public override int NumBytes => (int)this.pos;
public int PutCoeffs(int ctx, Vp8Residual residual) public int PutCoeffs(int ctx, Vp8Residual residual)
{ {
@ -395,67 +395,58 @@ internal class Vp8BitWriter : BitWriterBase
} }
/// <summary> /// <summary>
/// Writes the encoded image to the stream. /// Write the trunks before data trunk.
/// </summary> /// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="BitWriterBase.scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param> /// <param name="stream">The stream to write to.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="exifProfile">The exif profile.</param> /// <param name="exifProfile">The exif profile.</param>
/// <param name="xmpProfile">The XMP profile.</param> /// <param name="xmpProfile">The XMP profile.</param>
/// <param name="iccProfile">The color profile.</param> /// <param name="iccProfile">The color profile.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param> /// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
/// <param name="alphaData">The alpha channel data.</param> /// <param name="alphaData">The alpha channel data.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha data is compressed.</param> /// <param name="alphaDataIsCompressed">Indicates, if the alpha data is compressed.</param>
public void WriteEncodedImageToStream( public void WriteTrunksBeforeData(
Stream stream, Stream stream,
uint width,
uint height,
ExifProfile? exifProfile, ExifProfile? exifProfile,
XmpProfile? xmpProfile, XmpProfile? xmpProfile,
IccProfile? iccProfile, IccProfile? iccProfile,
uint width,
uint height,
bool hasAlpha, bool hasAlpha,
Span<byte> alphaData, Span<byte> alphaData,
bool alphaDataIsCompressed) bool alphaDataIsCompressed)
{ {
bool isVp8X = false; // Write file size later
byte[]? exifBytes = null; this.WriteRiffHeader(stream, 0);
byte[]? xmpBytes = null;
byte[]? iccProfileBytes = null;
uint riffSize = 0;
if (exifProfile != null)
{
isVp8X = true;
exifBytes = exifProfile.ToByteArray();
riffSize += MetadataChunkSize(exifBytes!);
}
if (xmpProfile != null) // Write VP8X, header if necessary.
bool isVp8X = exifProfile != null || xmpProfile != null || iccProfile != null || hasAlpha;
if (isVp8X)
{ {
isVp8X = true; this.WriteVp8XHeader(stream, exifProfile, xmpProfile, iccProfile, width, height, hasAlpha);
xmpBytes = xmpProfile.Data;
riffSize += MetadataChunkSize(xmpBytes!);
}
if (iccProfile != null) if (iccProfile != null)
{ {
isVp8X = true; this.WriteColorProfile(stream, iccProfile.ToByteArray());
iccProfileBytes = iccProfile.ToByteArray(); }
riffSize += MetadataChunkSize(iccProfileBytes);
}
if (hasAlpha) if (hasAlpha)
{ {
isVp8X = true; this.WriteAlphaChunk(stream, alphaData, alphaDataIsCompressed);
riffSize += AlphaChunkSize(alphaData); }
} }
}
if (isVp8X) /// <summary>
{ /// Writes the encoded image to the stream.
riffSize += ExtendedFileChunkSize; /// </summary>
} /// <param name="stream">The stream to write to.</param>
public void WriteEncodedImageToStream(Stream stream)
{
uint numBytes = (uint)this.NumBytes;
this.Finish();
uint numBytes = (uint)this.NumBytes();
int mbSize = this.enc.Mbw * this.enc.Mbh; int mbSize = this.enc.Mbw * this.enc.Mbh;
int expectedSize = (int)((uint)mbSize * 7 / 8); int expectedSize = (int)((uint)mbSize * 7 / 8);
@ -469,12 +460,10 @@ internal class Vp8BitWriter : BitWriterBase
uint pad = vp8Size & 1; uint pad = vp8Size & 1;
vp8Size += pad; vp8Size += pad;
// Compute RIFF size. // Emit header and partition #0
// At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. this.WriteVp8Header(stream, vp8Size);
riffSize += WebpConstants.TagSize + WebpConstants.ChunkHeaderSize + vp8Size; this.WriteFrameHeader(stream, size0);
// Emit headers and partition #0
this.WriteWebpHeaders(stream, size0, vp8Size, riffSize, isVp8X, width, height, exifProfile, xmpProfile, iccProfileBytes, hasAlpha, alphaData, alphaDataIsCompressed);
bitWriterPartZero.WriteToStream(stream); bitWriterPartZero.WriteToStream(stream);
// Write the encoded image to the stream. // Write the encoded image to the stream.
@ -483,16 +472,31 @@ internal class Vp8BitWriter : BitWriterBase
{ {
stream.WriteByte(0); stream.WriteByte(0);
} }
}
/// <summary>
/// Write the trunks after data trunk.
/// </summary>
/// <remarks>Think of it as a static method — none of the other members are called except for <see cref="BitWriterBase.scratchBuffer"/></remarks>
/// <param name="stream">The stream to write to.</param>
/// <param name="exifProfile">The exif profile.</param>
/// <param name="xmpProfile">The XMP profile.</param>
public void WriteTrunksAfterData(
Stream stream,
ExifProfile? exifProfile,
XmpProfile? xmpProfile)
{
if (exifProfile != null) if (exifProfile != null)
{ {
this.WriteMetadataProfile(stream, exifBytes, WebpChunkType.Exif); this.WriteMetadataProfile(stream, exifProfile.ToByteArray(), WebpChunkType.Exif);
} }
if (xmpProfile != null) if (xmpProfile != null)
{ {
this.WriteMetadataProfile(stream, xmpBytes, WebpChunkType.Xmp); this.WriteMetadataProfile(stream, xmpProfile.Data, WebpChunkType.Xmp);
} }
OverwriteFileSize(stream);
} }
private uint GeneratePartition0() private uint GeneratePartition0()
@ -512,7 +516,7 @@ internal class Vp8BitWriter : BitWriterBase
this.Finish(); this.Finish();
return (uint)this.NumBytes(); return (uint)this.NumBytes;
} }
private void WriteSegmentHeader() private void WriteSegmentHeader()
@ -662,43 +666,6 @@ internal class Vp8BitWriter : BitWriterBase
while (it.Next()); while (it.Next());
} }
private void WriteWebpHeaders(
Stream stream,
uint size0,
uint vp8Size,
uint riffSize,
bool isVp8X,
uint width,
uint height,
ExifProfile? exifProfile,
XmpProfile? xmpProfile,
byte[]? iccProfileBytes,
bool hasAlpha,
Span<byte> alphaData,
bool alphaDataIsCompressed)
{
this.WriteRiffHeader(stream, riffSize);
// Write VP8X, header if necessary.
if (isVp8X)
{
this.WriteVp8XHeader(stream, exifProfile, xmpProfile, iccProfileBytes, width, height, hasAlpha);
if (iccProfileBytes != null)
{
this.WriteColorProfile(stream, iccProfileBytes);
}
if (hasAlpha)
{
this.WriteAlphaChunk(stream, alphaData, alphaDataIsCompressed);
}
}
this.WriteVp8Header(stream, vp8Size);
this.WriteFrameHeader(stream, size0);
}
private void WriteVp8Header(Stream stream, uint size) private void WriteVp8Header(Stream stream, uint size)
{ {
Span<byte> buf = stackalloc byte[WebpConstants.TagSize]; Span<byte> buf = stackalloc byte[WebpConstants.TagSize];

10
src/ImageSharp/Formats/Webp/BitWriter/Vp8LBitWriter.cs

@ -47,6 +47,9 @@ internal class Vp8LBitWriter : BitWriterBase
{ {
} }
/// <inheritdoc/>
public override int NumBytes => this.cur + ((this.used + 7) >> 3);
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Vp8LBitWriter"/> class. /// Initializes a new instance of the <see cref="Vp8LBitWriter"/> class.
/// Used internally for cloning. /// Used internally for cloning.
@ -98,9 +101,6 @@ internal class Vp8LBitWriter : BitWriterBase
this.PutBits((uint)((bits << depth) | symbol), depth + nBits); this.PutBits((uint)((bits << depth) | symbol), depth + nBits);
} }
/// <inheritdoc/>
public override int NumBytes() => this.cur + ((this.used + 7) >> 3);
public Vp8LBitWriter Clone() public Vp8LBitWriter Clone()
{ {
byte[] clonedBuffer = new byte[this.Buffer.Length]; byte[] clonedBuffer = new byte[this.Buffer.Length];
@ -166,7 +166,7 @@ internal class Vp8LBitWriter : BitWriterBase
} }
this.Finish(); this.Finish();
uint size = (uint)this.NumBytes(); uint size = (uint)this.NumBytes;
size++; // One byte extra for the VP8L signature. size++; // One byte extra for the VP8L signature.
// Write RIFF header. // Write RIFF header.
@ -177,7 +177,7 @@ internal class Vp8LBitWriter : BitWriterBase
// Write VP8X, header if necessary. // Write VP8X, header if necessary.
if (isVp8X) if (isVp8X)
{ {
this.WriteVp8XHeader(stream, exifProfile, xmpProfile, iccBytes, width, height, hasAlpha); this.WriteVp8XHeader(stream, exifProfile, xmpProfile, iccProfile, width, height, hasAlpha);
if (iccBytes != null) if (iccBytes != null)
{ {

8
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -291,7 +291,7 @@ internal class Vp8LEncoder : IDisposable
// The image-stream will NOT contain any headers describing the image dimension, the dimension is already known. // The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
this.EncodeStream(frame); this.EncodeStream(frame);
this.bitWriter.Finish(); this.bitWriter.Finish();
int size = this.bitWriter.NumBytes(); int size = this.bitWriter.NumBytes;
if (size >= pixelCount) if (size >= pixelCount)
{ {
// Compressing would not yield in smaller data -> leave the data uncompressed. // Compressing would not yield in smaller data -> leave the data uncompressed.
@ -425,9 +425,9 @@ internal class Vp8LEncoder : IDisposable
lowEffort); lowEffort);
// If we are better than what we already have. // If we are better than what we already have.
if (isFirstConfig || this.bitWriter.NumBytes() < bestSize) if (isFirstConfig || this.bitWriter.NumBytes < bestSize)
{ {
bestSize = this.bitWriter.NumBytes(); bestSize = this.bitWriter.NumBytes;
BitWriterSwap(ref this.bitWriter, ref bitWriterBest); BitWriterSwap(ref this.bitWriter, ref bitWriterBest);
} }
@ -676,7 +676,7 @@ internal class Vp8LEncoder : IDisposable
this.StoreImageToBitMask(width, this.HistoBits, refsBest, histogramSymbols, huffmanCodes); this.StoreImageToBitMask(width, this.HistoBits, refsBest, histogramSymbols, huffmanCodes);
// Keep track of the smallest image so far. // Keep track of the smallest image so far.
if (isFirstIteration || (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes())) if (isFirstIteration || (bitWriterBest != null && this.bitWriter.NumBytes < bitWriterBest.NumBytes))
{ {
Vp8LBitWriter tmp = this.bitWriter; Vp8LBitWriter tmp = this.bitWriter;
this.bitWriter = bitWriterBest; this.bitWriter = bitWriterBest;

27
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Webp.BitWriter;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -408,16 +409,21 @@ internal class Vp8Encoder : IDisposable
} }
} }
this.bitWriter.WriteEncodedImageToStream( this.bitWriter.Finish();
this.bitWriter.WriteTrunksBeforeData(
stream, stream,
(uint)width,
(uint)height,
exifProfile, exifProfile,
xmpProfile, xmpProfile,
metadata.IccProfile, metadata.IccProfile,
(uint)width,
(uint)height,
hasAlpha, hasAlpha,
alphaData[..alphaDataSize], alphaData[..alphaDataSize],
this.alphaCompression && alphaCompressionSucceeded); this.alphaCompression && alphaCompressionSucceeded);
this.bitWriter.WriteEncodedImageToStream(stream);
this.bitWriter.WriteTrunksAfterData(stream, exifProfile, xmpProfile);
} }
finally finally
{ {
@ -862,10 +868,11 @@ internal class Vp8Encoder : IDisposable
this.ResetSegments(); this.ResetSegments();
} }
this.SegmentHeader.Size = (p[0] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(0, probas[1]))) + this.SegmentHeader.Size =
(p[1] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(1, probas[1]))) + (p[0] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(0, probas[1]))) +
(p[2] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(0, probas[2]))) + (p[1] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(1, probas[1]))) +
(p[3] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(1, probas[2]))); (p[2] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(0, probas[2]))) +
(p[3] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(1, probas[2])));
} }
else else
{ {
@ -1027,7 +1034,7 @@ internal class Vp8Encoder : IDisposable
it.NzToBytes(); it.NzToBytes();
int pos1 = this.bitWriter.NumBytes(); int pos1 = this.bitWriter.NumBytes;
if (i16) if (i16)
{ {
residual.Init(0, 1, this.Proba); residual.Init(0, 1, this.Proba);
@ -1054,7 +1061,7 @@ internal class Vp8Encoder : IDisposable
} }
} }
int pos2 = this.bitWriter.NumBytes(); int pos2 = this.bitWriter.NumBytes;
// U/V // U/V
residual.Init(0, 2, this.Proba); residual.Init(0, 2, this.Proba);
@ -1072,7 +1079,7 @@ internal class Vp8Encoder : IDisposable
} }
} }
int pos3 = this.bitWriter.NumBytes(); int pos3 = this.bitWriter.NumBytes;
it.LumaBits = pos2 - pos1; it.LumaBits = pos2 - pos1;
it.UvBits = pos3 - pos2; it.UvBits = pos3 - pos2;
it.BitCount[segment, i16 ? 1 : 0] += it.LumaBits; it.BitCount[segment, i16 ? 1 : 0] += it.LumaBits;

11
src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs

@ -76,7 +76,7 @@ internal static class WebpChunkParsingUtils
WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes"); WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes");
} }
if (!buffer.Slice(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes)) if (!buffer[..3].SequenceEqual(WebpConstants.Vp8HeaderMagicBytes))
{ {
WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found"); WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found");
} }
@ -111,14 +111,7 @@ internal static class WebpChunkParsingUtils
PartitionLength = partitionLength PartitionLength = partitionLength
}; };
Vp8BitReader bitReader = new( Vp8BitReader bitReader = new(stream, remaining, memoryAllocator, partitionLength) { Remaining = remaining };
stream,
remaining,
memoryAllocator,
partitionLength)
{
Remaining = remaining
};
return new() return new()
{ {

4
src/ImageSharp/Metadata/Profiles/ICC/IccProfile.cs

@ -158,8 +158,7 @@ public sealed class IccProfile : IDeepCloneable<IccProfile>
Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) && Enum.IsDefined(typeof(IccColorSpaceType), this.Header.DataColorSpace) &&
Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) && Enum.IsDefined(typeof(IccColorSpaceType), this.Header.ProfileConnectionSpace) &&
Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) && Enum.IsDefined(typeof(IccRenderingIntent), this.Header.RenderingIntent) &&
this.Header.Size >= minSize && this.Header.Size is >= minSize and < maxSize;
this.Header.Size < maxSize;
} }
/// <summary> /// <summary>
@ -175,7 +174,6 @@ public sealed class IccProfile : IDeepCloneable<IccProfile>
return copy; return copy;
} }
IccWriter writer = new();
return IccWriter.Write(this); return IccWriter.Write(this);
} }

Loading…
Cancel
Save