Browse Source

Encode EXR image with usigned int pixel data

pull/3096/head
Brian Popow 5 years ago
parent
commit
7e7472d575
  1. 2
      src/ImageSharp/Formats/OpenExr/ExrChannelInfo.cs
  2. 14
      src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs
  3. 98
      src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs
  4. 2
      src/ImageSharp/Image.Decode.cs

2
src/ImageSharp/Formats/OpenExr/ExrChannelInfo.cs

@ -1,10 +1,12 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.OpenExr namespace SixLabors.ImageSharp.Formats.OpenExr
{ {
[DebuggerDisplay("Name: {ChannelName}, PixelType: {PixelType}")]
[StructLayout(LayoutKind.Sequential, Pack = 1)] [StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct ExrChannelInfo internal readonly struct ExrChannelInfo
{ {

14
src/ImageSharp/Formats/OpenExr/ExrDecoderCore.cs

@ -106,8 +106,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
{ {
using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 3); using IMemoryOwner<float> rowBuffer = this.memoryAllocator.Allocate<float>(this.Width * 3);
Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); Span<float> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width);
Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); Span<float> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width);
Span<float> greenPixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); Span<float> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width);
TPixel color = default; TPixel color = default;
for (int y = 0; y < this.Height; y++) for (int y = 0; y < this.Height; y++)
@ -193,8 +193,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
{ {
using IMemoryOwner<uint> rowBuffer = this.memoryAllocator.Allocate<uint>(this.Width * 3); using IMemoryOwner<uint> rowBuffer = this.memoryAllocator.Allocate<uint>(this.Width * 3);
Span<uint> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width); Span<uint> redPixelData = rowBuffer.GetSpan().Slice(0, this.Width);
Span<uint> bluePixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width); Span<uint> greenPixelData = rowBuffer.GetSpan().Slice(this.Width, this.Width);
Span<uint> greenPixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width); Span<uint> bluePixelData = rowBuffer.GetSpan().Slice(this.Width * 2, this.Width);
TPixel color = default; TPixel color = default;
for (int y = 0; y < this.Height; y++) for (int y = 0; y < this.Height; y++)
@ -389,11 +389,11 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
header.Compression = (ExrCompression)stream.ReadByte(); header.Compression = (ExrCompression)stream.ReadByte();
break; break;
case ExrConstants.AttributeNames.DataWindow: case ExrConstants.AttributeNames.DataWindow:
ExrBox2i dataWindow = this.ReadBox2i(stream); ExrBox2i dataWindow = this.ReadBoxInteger(stream);
header.DataWindow = dataWindow; header.DataWindow = dataWindow;
break; break;
case ExrConstants.AttributeNames.DisplayWindow: case ExrConstants.AttributeNames.DisplayWindow:
ExrBox2i displayWindow = this.ReadBox2i(stream); ExrBox2i displayWindow = this.ReadBoxInteger(stream);
header.DisplayWindow = displayWindow; header.DisplayWindow = displayWindow;
break; break;
case ExrConstants.AttributeNames.LineOrder: case ExrConstants.AttributeNames.LineOrder:
@ -441,7 +441,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
return new ExrAttribute(attributeName, attributeType, attributeSize); return new ExrAttribute(attributeName, attributeType, attributeSize);
} }
private ExrBox2i ReadBox2i(BufferedReadStream stream) private ExrBox2i ReadBoxInteger(BufferedReadStream stream)
{ {
stream.Read(this.buffer, 0, 4); stream.Read(this.buffer, 0, 4);
int xMin = BinaryPrimitives.ReadInt32LittleEndian(this.buffer); int xMin = BinaryPrimitives.ReadInt32LittleEndian(this.buffer);

98
src/ImageSharp/Formats/OpenExr/ExrEncoderCore.cs

@ -6,6 +6,7 @@ using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -57,6 +58,8 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer;
ImageMetadata metadata = image.Metadata; ImageMetadata metadata = image.Metadata;
ExrMetadata exrMetadata = metadata.GetExrMetadata(); ExrMetadata exrMetadata = metadata.GetExrMetadata();
this.pixelType ??= exrMetadata.PixelType; this.pixelType ??= exrMetadata.PixelType;
@ -86,7 +89,7 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
// Version number. // Version number.
this.buffer[0] = 2; this.buffer[0] = 2;
// Second, third and fourth bytes store info about the image, all set to zero. // Second, third and fourth bytes store info about the image, set all to default: zero.
this.buffer[1] = 0; this.buffer[1] = 0;
this.buffer[2] = 0; this.buffer[2] = 0;
this.buffer[3] = 0; this.buffer[3] = 0;
@ -95,18 +98,33 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
// Write EXR header. // Write EXR header.
this.WriteHeader(stream, header); this.WriteHeader(stream, header);
// Write offsets to each pixel row.
int bytesPerChannel = this.pixelType == ExrPixelType.Half ? 2 : 4;
int numberOfChannels = 3;
uint rowSizeBytes = (uint)(width * numberOfChannels * bytesPerChannel);
this.WriteRowOffsets(stream, height, rowSizeBytes);
// Write pixel data. // Write pixel data.
Buffer2D<TPixel> pixels = image.Frames.RootFrame.PixelBuffer; switch (this.pixelType)
{
case ExrPixelType.Half:
case ExrPixelType.Float:
this.EncodeFloatingPointPixelData(stream, pixels, width, height, rowSizeBytes);
break;
case ExrPixelType.UnsignedInt:
this.EncodeUnsignedIntPixelData(stream, pixels, width, height, rowSizeBytes);
break;
}
}
private void EncodeFloatingPointPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<float> rgbBuffer = this.memoryAllocator.Allocate<float>(width * 3); using IMemoryOwner<float> rgbBuffer = this.memoryAllocator.Allocate<float>(width * 3);
Span<float> redBuffer = rgbBuffer.GetSpan().Slice(0, width); Span<float> redBuffer = rgbBuffer.GetSpan().Slice(0, width);
Span<float> blueBuffer = rgbBuffer.GetSpan().Slice(width, width); Span<float> greenBuffer = rgbBuffer.GetSpan().Slice(width, width);
Span<float> greenBuffer = rgbBuffer.GetSpan().Slice(width * 2, width); Span<float> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width);
// Write offsets to each pixel row.
int bytesPerPixel = this.pixelType == ExrPixelType.Half ? 2 : 4;
int numberOfChannels = 3;
uint rowSizeBytes = (uint)(width * numberOfChannels * bytesPerPixel);
this.WriteRowOffsets(stream, height, rowSizeBytes);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y); Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
@ -139,6 +157,41 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
} }
} }
private void EncodeUnsignedIntPixelData<TPixel>(Stream stream, Buffer2D<TPixel> pixels, int width, int height, uint rowSizeBytes)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<uint> rgbBuffer = this.memoryAllocator.Allocate<uint>(width * 3);
Span<uint> redBuffer = rgbBuffer.GetSpan().Slice(0, width);
Span<uint> greenBuffer = rgbBuffer.GetSpan().Slice(width, width);
Span<uint> blueBuffer = rgbBuffer.GetSpan().Slice(width * 2, width);
var rgb = default(Rgb96);
for (int y = 0; y < height; y++)
{
Span<TPixel> pixelRowSpan = pixels.DangerousGetRowSpan(y);
for (int x = 0; x < width; x++)
{
var vector4 = pixelRowSpan[x].ToVector4();
rgb.FromVector4(vector4);
redBuffer[x] = rgb.R;
greenBuffer[x] = rgb.G;
blueBuffer[x] = rgb.B;
}
// Write row index.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, (uint)y);
stream.Write(this.buffer.AsSpan(0, 4));
// Write pixel row data size.
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, rowSizeBytes);
stream.Write(this.buffer.AsSpan(0, 4));
this.WriteUnsignedIntRow(stream, width, blueBuffer, greenBuffer, redBuffer);
}
}
private void WriteHeader(Stream stream, ExrHeader header) private void WriteHeader(Stream stream, ExrHeader header)
{ {
this.WriteChannels(stream, header.Channels); this.WriteChannels(stream, header.Channels);
@ -188,6 +241,24 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
} }
} }
private void WriteUnsignedIntRow(Stream stream, int width, Span<uint> blueBuffer, Span<uint> greenBuffer, Span<uint> redBuffer)
{
for (int x = 0; x < width; x++)
{
this.WriteUnsignedInt(stream, blueBuffer[x]);
}
for (int x = 0; x < width; x++)
{
this.WriteUnsignedInt(stream, greenBuffer[x]);
}
for (int x = 0; x < width; x++)
{
this.WriteUnsignedInt(stream, redBuffer[x]);
}
}
private void WriteRowOffsets(Stream stream, int height, uint rowSizeBytes) private void WriteRowOffsets(Stream stream, int height, uint rowSizeBytes)
{ {
ulong startOfPixelData = (ulong)stream.Position + (8 * (ulong)height); ulong startOfPixelData = (ulong)stream.Position + (8 * (ulong)height);
@ -325,17 +396,26 @@ namespace SixLabors.ImageSharp.Formats.OpenExr
stream.Write(this.buffer.AsSpan(0, 4)); stream.Write(this.buffer.AsSpan(0, 4));
} }
[MethodImpl(InliningOptions.ShortMethod)]
private unsafe void WriteSingle(Stream stream, float value) private unsafe void WriteSingle(Stream stream, float value)
{ {
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, *(int*)&value); BinaryPrimitives.WriteInt32LittleEndian(this.buffer, *(int*)&value);
stream.Write(this.buffer.AsSpan(0, 4)); stream.Write(this.buffer.AsSpan(0, 4));
} }
[MethodImpl(InliningOptions.ShortMethod)]
private void WriteHalfSingle(Stream stream, float value) private void WriteHalfSingle(Stream stream, float value)
{ {
ushort valueAsShort = HalfTypeHelper.Pack(value); ushort valueAsShort = HalfTypeHelper.Pack(value);
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, valueAsShort); BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, valueAsShort);
stream.Write(this.buffer.AsSpan(0, 2)); stream.Write(this.buffer.AsSpan(0, 2));
} }
[MethodImpl(InliningOptions.ShortMethod)]
private void WriteUnsignedInt(Stream stream, uint value)
{
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value);
stream.Write(this.buffer.AsSpan(0, 4));
}
} }
} }

2
src/ImageSharp/Image.Decode.cs

@ -2,9 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;

Loading…
Cancel
Save