Browse Source

First attempt parsing minimal image info, infoIdentify works at least for reading the image dimensions so far

pull/1147/head
Brian Popow 6 years ago
parent
commit
89080b9e5b
  1. 6
      src/ImageSharp/Configuration.cs
  2. 14
      src/ImageSharp/Formats/WebP/ExtendedDecoder.cs
  3. 16
      src/ImageSharp/Formats/WebP/IWebPDecoderOptions.cs
  4. 8
      src/ImageSharp/Formats/WebP/Readme.md
  5. 14
      src/ImageSharp/Formats/WebP/SimpleLosslessDecoder.cs
  6. 14
      src/ImageSharp/Formats/WebP/SimpleLossyDecoder.cs
  7. 65
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  8. 21
      src/ImageSharp/Formats/WebP/WebPDecoder.cs
  9. 187
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  10. 18
      src/ImageSharp/Formats/WebP/WebPDecoderCoreBase.cs
  11. 22
      src/ImageSharp/Formats/WebP/WebPImageFormatDetector.cs
  12. 27
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs
  13. 20
      src/ImageSharp/Formats/WebP/WebPThrowHelper.cs
  14. 18
      src/ImageSharp/Formats/WebP/WebpConfigurationModule.cs

6
src/ImageSharp/Configuration.cs

@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.WebP;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Processing;
using SixLabors.Memory;
@ -158,7 +159,8 @@ namespace SixLabors.ImageSharp
new PngConfigurationModule(),
new JpegConfigurationModule(),
new GifConfigurationModule(),
new BmpConfigurationModule());
new BmpConfigurationModule(),
new WebPConfigurationModule());
}
}
}
}

14
src/ImageSharp/Formats/WebP/ExtendedDecoder.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.WebP
{
class ExtendedDecoderCore : WebPDecoderCoreBase
{
public override Image<TPixel> Decode<TPixel>(Stream stream)
=> throw new NotImplementedException();
}
}

16
src/ImageSharp/Formats/WebP/IWebPDecoderOptions.cs

@ -0,0 +1,16 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Image decoder for generating an image out of a webp stream.
/// </summary>
internal interface IWebPDecoderOptions
{
/// <summary>
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
}
}

8
src/ImageSharp/Formats/WebP/Readme.md

@ -1,4 +1,8 @@
= WebP Format
# WebP Format
Reference implementation, specification and stuff like that:
https://developers.google.com/speed/webp
- [google webp introduction](https://developers.google.com/speed/webp)
- [WebP Spec 0.2](https://chromium.googlesource.com/webm/libwebp/+/v0.2.0/doc/webp-container-spec.txt)
- [WebP VP8 chunk Spec](http://tools.ietf.org/html/rfc6386)
- [WebP filefront](https://wiki.fileformat.com/image/webp/)

14
src/ImageSharp/Formats/WebP/SimpleLosslessDecoder.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.WebP
{
class SimpleLosslessDecoder : WebPDecoderCoreBase
{
public override Image<TPixel> Decode<TPixel>(Stream stream)
=> throw new NotImplementedException();
}
}

14
src/ImageSharp/Formats/WebP/SimpleLossyDecoder.cs

@ -1,14 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.IO;
namespace SixLabors.ImageSharp.Formats.WebP
{
class SimpleLossyDecoder : WebPDecoderCoreBase
{
public override Image<TPixel> Decode<TPixel>(Stream stream)
=> throw new NotImplementedException();
}
}

65
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -16,5 +16,70 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// The list of mimetypes that equate to a jpeg.
/// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/webp", };
/// <summary>
/// The header bytes identifying RIFF file.
/// </summary>
public static readonly byte[] FourCcBytes =
{
0x52, // R
0x49, // I
0x46, // F
0x46 // F
};
/// <summary>
/// The header bytes identifying a WebP.
/// </summary>
public static readonly byte[] WebPHeader =
{
0x57, // W
0x45, // E
0x42, // B
0x50 // P
};
/// <summary>
/// Header signaling the use of VP8 video format.
/// </summary>
public static readonly byte[] Vp8Header =
{
0x56, // V
0x50, // P
0x38, // 8
0x20, // Space
};
/// <summary>
/// Header for a extended-VP8 chunk.
/// </summary>
public static readonly byte[] Vp8XHeader =
{
0x56, // V
0x50, // P
0x38, // 8
0x88, // X
};
public static readonly byte LossLessFlag = 0x4C; // L
/// <summary>
/// VP8 header, signaling the use of VP8L lossless format.
/// </summary>
public static readonly byte[] Vp8LHeader =
{
0x56, // V
0x50, // P
0x38, // 8
LossLessFlag // L
};
public static readonly byte[] AlphaHeader =
{
0x41, // A
0x4C, // L
0x50, // P
0x48, // H
};
}
}

21
src/ImageSharp/Formats/WebP/WebPDecoder.cs

@ -9,20 +9,31 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Image decoder for generating an image out of a webp stream.
/// </summary>
public sealed class WebPDecoder : IImageDecoder
public sealed class WebPDecoder : IImageDecoder, IWebPDecoderOptions, IImageInfoDetector
{
/// <summary>
/// Gets or sets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : struct, IPixel<TPixel>
{
// TODO: [brianpopow] parse chunks and decide which decoder (subclass of WebPDecoderCoreBase) to use
throw new System.NotImplementedException();
Guard.NotNull(stream, nameof(stream));
return new WebPDecoderCore(configuration, this).Decode<TPixel>(stream);
}
/// <inheritdoc/>
public Image Decode(Configuration configuration, Stream stream)
public IImageInfo Identify(Configuration configuration, Stream stream)
{
throw new System.NotImplementedException();
Guard.NotNull(stream, nameof(stream));
return new WebPDecoderCore(configuration, this).Identify(stream);
}
/// <inheritdoc />
public Image Decode(Configuration configuration, Stream stream) => this.Decode<Rgba32>(configuration, stream);
}
}

187
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -0,0 +1,187 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Performs the bitmap decoding operation.
/// </summary>
internal sealed class WebPDecoderCore
{
/// <summary>
/// Reusable buffer.
/// </summary>
private readonly byte[] buffer = new byte[4];
/// <summary>
/// The global configuration.
/// </summary>
private readonly Configuration configuration;
/// <summary>
/// Used for allocating memory during processing operations.
/// </summary>
private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// The bitmap decoder options.
/// </summary>
private readonly IWebPDecoderOptions options;
/// <summary>
/// The stream to decode from.
/// </summary>
private Stream currentStream;
/// <summary>
/// The metadata.
/// </summary>
private ImageMetadata metadata;
/// <summary>
/// Initializes a new instance of the <see cref="WebPDecoderCore"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="options">The options.</param>
public WebPDecoderCore(Configuration configuration, IWebPDecoderOptions options)
{
this.configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator;
this.options = options;
}
public Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>
{
var metadata = new ImageMetadata();
WebPMetadata webpMetadata = metadata.GetFormatMetadata(WebPFormat.Instance);
this.currentStream = stream;
uint chunkSize = this.ReadImageHeader();
WebPImageInfo imageInfo = this.ReadVp8Info();
// TODO: there can be optional chunks after that, like EXIF.
var image = new Image<TPixel>(this.configuration, imageInfo.Width, imageInfo.Height, this.metadata);
Buffer2D<TPixel> pixels = image.GetRootFramePixelBuffer();
if (imageInfo.IsLooseLess)
{
ReadSimpleLossless(pixels, image.Width, image.Height);
}
else
{
ReadSimpleLossy(pixels, image.Width, image.Height);
}
return image;
}
/// <summary>
/// Reads the raw image information from the specified stream.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public IImageInfo Identify(Stream stream)
{
var metadata = new ImageMetadata();
WebPMetadata webpMetadata = metadata.GetFormatMetadata(WebPFormat.Instance);
this.currentStream = stream;
this.ReadImageHeader();
WebPImageInfo imageInfo = this.ReadVp8Info();
// TODO: not sure yet where to get this info. Assuming 24 bits for now.
int bitsPerPixel = 24;
return new ImageInfo(new PixelTypeInfo(bitsPerPixel), imageInfo.Width, imageInfo.Height, this.metadata);
}
private uint ReadImageHeader()
{
// Skip FourCC header, we already know its a RIFF file at this point.
this.currentStream.Skip(4);
// Read Chunk size.
this.currentStream.Read(this.buffer, 0, 4);
uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// Skip 'WEBP' from the header.
this.currentStream.Skip(4);
return chunkSize;
}
private WebPImageInfo ReadVp8Info()
{
// Read VP8 chunk header.
this.currentStream.Read(this.buffer, 0, 4);
if (this.buffer.AsSpan().SequenceEqual(WebPConstants.AlphaHeader))
{
WebPThrowHelper.ThrowImageFormatException("Alpha channel is not yet supported");
}
if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8XHeader))
{
WebPThrowHelper.ThrowImageFormatException("Vp8X is not yet supported");
}
if (!(this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header)
|| this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader)))
{
WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header");
}
bool isLossLess = this.buffer[3] == WebPConstants.LossLessFlag;
// VP8 data size.
this.currentStream.Read(this.buffer, 0, 3);
this.buffer[3] = 0;
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// https://tools.ietf.org/html/rfc6386#page-30
var imageInfo = new byte[11];
this.currentStream.Read(imageInfo, 0, imageInfo.Length);
int tmp = (imageInfo[2] << 16) | (imageInfo[1] << 8) | imageInfo[0];
int isKeyFrame = tmp & 0x1;
int version = (tmp >> 1) & 0x7;
int showFrame = (tmp >> 4) & 0x1;
// TODO: Get horizontal and vertical scale
int width = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(7)) & 0x3fff;
int height = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(9)) & 0x3fff;
return new WebPImageInfo()
{
Width = width,
Height = height,
IsLooseLess = isLossLess,
Version = version,
DataSize = dataSize
};
}
private void ReadSimpleLossy<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
// TODO: implement decoding
}
private void ReadSimpleLossless<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
// TODO: implement decoding
}
private void ReadExtended<TPixel>(Buffer2D<TPixel> pixels, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
// TODO: implement decoding
}
}
}

18
src/ImageSharp/Formats/WebP/WebPDecoderCoreBase.cs

@ -1,18 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Base class for the WebP image decoders.
/// </summary>
public abstract class WebPDecoderCoreBase
{
public abstract Image<TPixel> Decode<TPixel>(Stream stream)
where TPixel : struct, IPixel<TPixel>;
}
}

22
src/ImageSharp/Formats/WebP/WebPImageFormatDetector.cs

@ -6,7 +6,7 @@ using System;
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Detects WebP file headers
/// Detects WebP file headers.
/// </summary>
public sealed class WebPImageFormatDetector : IImageFormatDetector
{
@ -26,20 +26,24 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.IsWebPFile(header);
}
/// <summary>
/// Checks, if the header starts with a valid RIFF FourCC.
/// </summary>
/// <param name="header">The header bytes.</param>
/// <returns>True, if its a valid RIFF FourCC.</returns>
private bool IsRiffContainer(ReadOnlySpan<byte> header)
{
return header[0] == 0x52 && // R
header[1] == 0x49 && // I
header[2] == 0x46 && // F
header[3] == 0x46; // F
return header.Slice(0, 4).SequenceEqual(WebPConstants.FourCcBytes);
}
/// <summary>
/// Checks if 'WEBP' is present in the header.
/// </summary>
/// <param name="header">The header bytes.</param>
/// <returns>True, if its a webp file.</returns>
private bool IsWebPFile(ReadOnlySpan<byte> header)
{
return header[8] == 0x57 && // W
header[9] == 0x45 && // E
header[10] == 0x42 && // B
header[11] == 0x50; // P
return header.Slice(8, 4).SequenceEqual(WebPConstants.WebPHeader);
}
}
}

27
src/ImageSharp/Formats/WebP/WebPImageInfo.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class WebPImageInfo
{
/// <summary>
/// Gets or sets the bitmap width in pixels (signed integer).
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the bitmap height in pixels (signed integer).
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets whether this image uses a looseless compression.
/// </summary>
public bool IsLooseLess { get; set; }
public int Version { get; set; }
public uint DataSize { get; set; }
}
}

20
src/ImageSharp/Formats/WebP/WebPThrowHelper.cs

@ -0,0 +1,20 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.WebP
{
internal static class WebPThrowHelper
{
/// <summary>
/// Cold path optimization for throwing <see cref="ImageFormatException"/>-s
/// </summary>
/// <param name="errorMessage">The error message for the exception.</param>
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowImageFormatException(string errorMessage)
{
throw new ImageFormatException(errorMessage);
}
}
}

18
src/ImageSharp/Formats/WebP/WebpConfigurationModule.cs

@ -0,0 +1,18 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// Registers the image encoders, decoders and mime type detectors for the webp format.
/// </summary>
public sealed class WebPConfigurationModule : IConfigurationModule
{
/// <inheritdoc/>
public void Configure(Configuration configuration)
{
configuration.ImageFormatsManager.SetDecoder(WebPFormat.Instance, new WebPDecoder());
configuration.ImageFormatsManager.AddImageFormatDetector(new WebPImageFormatDetector());
}
}
}
Loading…
Cancel
Save