mirror of https://github.com/SixLabors/ImageSharp
14 changed files with 372 additions and 78 deletions
@ -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(); |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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/) |
|||
|
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
} |
|||
@ -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>; |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue