Browse Source

Merge branch 'master' into colorspace-transforms

pull/595/head
James Jackson-South 8 years ago
parent
commit
215dedff22
  1. 11
      README.md
  2. 35
      appveyor.yml
  3. 14
      build.ps1
  4. 67
      run-tests.ps1
  5. 1
      src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
  6. 2
      src/ImageSharp/ColorSpaces/CieLab.cs
  7. 7
      src/ImageSharp/ColorSpaces/CieLch.cs
  8. 7
      src/ImageSharp/ColorSpaces/CieLchuv.cs
  9. 7
      src/ImageSharp/ColorSpaces/CieLuv.cs
  10. 7
      src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs
  11. 7
      src/ImageSharp/ColorSpaces/CieXyy.cs
  12. 7
      src/ImageSharp/ColorSpaces/CieXyz.cs
  13. 7
      src/ImageSharp/ColorSpaces/Cmyk.cs
  14. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CIeLchToCieLabConverter.cs
  15. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieLchConverter.cs
  16. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs
  17. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuvToCieLuvConverter.cs
  18. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieLchuvConverter.cs
  19. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieXyzConverter.cs
  20. 2
      src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToCieLabConverter.cs
  21. 9
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs
  22. 7
      src/ImageSharp/ColorSpaces/Conversion/Implementation/RgbWorkingSpace.cs
  23. 7
      src/ImageSharp/ColorSpaces/Hsl.cs
  24. 7
      src/ImageSharp/ColorSpaces/Hsv.cs
  25. 7
      src/ImageSharp/ColorSpaces/HunterLab.cs
  26. 7
      src/ImageSharp/ColorSpaces/LinearRgb.cs
  27. 7
      src/ImageSharp/ColorSpaces/Lms.cs
  28. 7
      src/ImageSharp/ColorSpaces/Rgb.cs
  29. 7
      src/ImageSharp/ColorSpaces/YCbCr.cs
  30. 23
      src/ImageSharp/Common/Helpers/DebugGuard.cs
  31. 4
      src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
  32. 2
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  33. 2
      src/ImageSharp/Formats/Bmp/BmpConstants.cs
  34. 4
      src/ImageSharp/Formats/Bmp/BmpDecoder.cs
  35. 16
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  36. 4
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  37. 17
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  38. 28
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  39. 14
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  40. 40
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  41. 37
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  42. 62
      src/ImageSharp/Formats/Gif/LzwEncoder.cs
  43. 64
      src/ImageSharp/Formats/Gif/PackedField.cs
  44. 23
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  45. 12
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  46. 7
      src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs
  47. 9
      src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs
  48. 11
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  49. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs
  50. 11
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  51. 9
      src/ImageSharp/Formats/Png/PngChunk.cs
  52. 97
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  53. 24
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  54. 48
      src/ImageSharp/Formats/Png/PngHeader.cs
  55. 1
      src/ImageSharp/IImageFrameCollection.cs
  56. 8
      src/ImageSharp/Image.FromStream.cs
  57. 5
      src/ImageSharp/Image.LoadPixelData.cs
  58. 33
      src/ImageSharp/ImageExtensions.cs
  59. 3
      src/ImageSharp/ImageFrame.LoadPixelData.cs
  60. 2
      src/ImageSharp/ImageFrameCollection.cs
  61. 34
      src/ImageSharp/ImageFrame{TPixel}.cs
  62. 4
      src/ImageSharp/ImageSharp.csproj
  63. 3
      src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs
  64. 16
      src/ImageSharp/Memory/SpanHelper.cs
  65. 25
      src/ImageSharp/MetaData/ImageProperty.cs
  66. 50
      src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
  67. 60
      src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
  68. 394
      src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
  69. 281
      src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
  70. 191
      src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
  71. 410
      src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
  72. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs
  73. 2
      src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs
  74. 18
      src/ImageSharp/PixelFormats/Alpha8.cs
  75. 20
      src/ImageSharp/PixelFormats/Argb32.cs
  76. 20
      src/ImageSharp/PixelFormats/Bgr24.cs
  77. 16
      src/ImageSharp/PixelFormats/Bgr565.cs
  78. 19
      src/ImageSharp/PixelFormats/Bgra32.cs
  79. 18
      src/ImageSharp/PixelFormats/Bgra4444.cs
  80. 28
      src/ImageSharp/PixelFormats/Bgra5551.cs
  81. 28
      src/ImageSharp/PixelFormats/Byte4.cs
  82. 6
      src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs
  83. 112
      src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs
  84. 32
      src/ImageSharp/PixelFormats/HalfSingle.cs
  85. 4
      src/ImageSharp/PixelFormats/HalfTypeHelper.cs
  86. 35
      src/ImageSharp/PixelFormats/HalfVector2.cs
  87. 37
      src/ImageSharp/PixelFormats/HalfVector4.cs
  88. 14
      src/ImageSharp/PixelFormats/IPixel.cs
  89. 5
      src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs
  90. 37
      src/ImageSharp/PixelFormats/NormalizedByte2.cs
  91. 37
      src/ImageSharp/PixelFormats/NormalizedByte4.cs
  92. 35
      src/ImageSharp/PixelFormats/NormalizedShort2.cs
  93. 37
      src/ImageSharp/PixelFormats/NormalizedShort4.cs
  94. 365
      src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs
  95. 42
      src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs
  96. 26
      src/ImageSharp/PixelFormats/Rg32.cs
  97. 19
      src/ImageSharp/PixelFormats/Rgb24.cs
  98. 16
      src/ImageSharp/PixelFormats/Rgba1010102.cs
  99. 17
      src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs
  100. 18
      src/ImageSharp/PixelFormats/Rgba32.cs

11
README.md

@ -24,6 +24,10 @@ Compared to `System.Drawing` we have been able to develop something much more fl
Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
### Documentation
For all SixLabors projects, including ImageSharp:
https://sixlabors.github.io/docs/
### Installation
Install stable releases via Nuget; development releases are available via MyGet.
@ -61,8 +65,6 @@ The **ImageSharp** library is made up of multiple packages:
### API
API documentation is available at [https://sixlabors.github.io/docs/](https://sixlabors.github.io/docs/)
Our API is designed to be simple to consume. Here's an example of the code required to resize an image using the default Bicubic resampler then turn the colors into their grayscale equivalent using the BT709 standard matrix.
On platforms supporting netstandard 1.3+
@ -106,7 +108,10 @@ using (Image<Rgba32> image = new Image<Rgba32>(400, 400))
`Rgba32` is our default PixelFormat, equivalent to `System.Drawing Color`. For advanced pixel format usage there are multiple [PixelFormat implementations](https://github.com/SixLabors/ImageSharp/tree/master/src/ImageSharp/PixelFormats) available allowing developers to implement their own color models in the same manner as Microsoft XNA Game Studio and MonoGame.
**Check out this [blog post](https://sixlabors.com/blog/announcing-imagesharp-beta-1/) or our [Samples Repository](https://github.com/SixLabors/Samples/tree/master/ImageSharp) for more examples!**
For more examples check out:
- [Our Documentation](https://sixlabors.github.io/docs/)
- Our [Samples Repository](https://github.com/SixLabors/Samples/tree/master/ImageSharp)
- The [beta1 blog post](https://sixlabors.com/blog/announcing-imagesharp-beta-1/)
### Manual build

35
appveyor.yml

@ -4,6 +4,39 @@ image: Visual Studio 2017
# prevent the double build when a branch has an active PR
skip_branch_with_pr: true
environment:
matrix:
- target_framework: net471
is_32bit: True
- target_framework: net462
is_32bit: False
- target_framework: net462
is_32bit: True
- target_framework: net471
is_32bit: False
- target_framework: netcoreapp2.0
is_32bit: False
#- target_framework: netcoreapp2.0 # As far as I understand, 32 bit test execution is not supported by "dotnet xunit"
# is_32bit: True
#- target_framework: mono
# is_32bit: False
#- target_framework: mono
# is_32bit: True
#- target_framework: net47
# is_32bit: False
#- target_framework: net47
# is_32bit: True
install:
- ps: |
if ($env:target_framework -eq "mono") {
if ($env:is_32bit -eq "True") {
cinst mono --x86
} else {
cinst mono
}
}
before_build:
- git submodule -q update --init
- cmd: dotnet --version
@ -12,7 +45,7 @@ build_script:
- cmd: build.cmd
test_script:
- tests\CodeCoverage\CodeCoverage.cmd
- ps: .\run-tests.ps1 $env:target_framework $env:is_32bit
after_test:
- cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%APPVEYOR_BUILD_VERSION%.nupkg"

14
build.ps1

@ -100,9 +100,17 @@ dotnet build -c Release /p:packageversion=$version
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
if ( $env:CI -ne "True") {
dotnet test ./tests/ImageSharp.Tests/ImageSharp.Tests.csproj --no-build -c Release
}
#
# TODO: DO WE NEED TO RUN TESTS IMPLICITLY?
#
# if ( $env:CI -ne "True") {
# cd ./tests/ImageSharp.Tests/
# dotnet xunit -nobuild -c Release -f netcoreapp2.0 --fx-version 2.0.0
# ./RunExtendedTests.cmd
# cd ../..
# }
#
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
Write-Host "Packaging projects"

67
run-tests.ps1

@ -0,0 +1,67 @@
param(
[string]$targetFramework,
[string]$is32Bit = "False"
)
if (!$targetFramework){
Write-Host "run-tests.ps1 ERROR: targetFramework is undefined!"
exit 1
}
function VerifyPath($path, $errorMessage) {
if (!(Test-Path -Path $path)) {
Write-Host "run-tests.ps1 $errorMessage `n $xunitRunnerPath"
exit 1
}
}
if ( ($targetFramework -eq "netcoreapp2.0") -and ($env:CI -eq "True") -and ($is32Bit -ne "True")) {
# We execute CodeCoverage.cmd only for one specific job on CI (netcoreapp2.0 + 64bit )
$testRunnerCmd = ".\tests\CodeCoverage\CodeCoverage.cmd"
}
elseif ($targetFramework -eq "mono") {
$testDllPath = "$PSScriptRoot\tests\ImageSharp.Tests\bin\Release\net462\SixLabors.ImageSharp.Tests.dll"
VerifyPath($testDllPath, "test dll missing:")
$xunitRunnerPath = "${env:HOMEPATH}\.nuget\packages\xunit.runner.console\2.3.1\tools\net452\"
VerifyPath($xunitRunnerPath, "xunit console runner is missing on path:")
cd "$xunitRunnerPath"
if ($is32Bit -ne "True") {
$monoPath = "${env:PROGRAMFILES}\Mono\bin\mono.exe"
}
else {
$monoPath = "${env:ProgramFiles(x86)}\Mono\bin\mono.exe"
}
VerifyPath($monoPath, "mono runtime missing:")
$testRunnerCmd = "& `"${monoPath}`" .\xunit.console.exe `"${testDllPath}`""
}
else {
cd .\tests\ImageSharp.Tests
$xunitArgs = "-nobuild -c Release -framework $targetFramework"
if ($targetFramework -eq "netcoreapp2.0") {
# There were issues matching the correct installed runtime if we do not specify it explicitly:
$xunitArgs += " --fx-version 2.0.0"
}
if ($is32Bit -eq "True") {
$xunitArgs += " -x86"
}
$testRunnerCmd = "dotnet xunit $xunitArgs"
}
Write-Host "running:"
Write-Host $testRunnerCmd
Write-Host "..."
Invoke-Expression $testRunnerCmd
cd $PSScriptRoot
exit $LASTEXITCODE

1
src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

@ -6,6 +6,7 @@
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
<Authors>SixLabors and contributors</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0</TargetFrameworks>
<LangVersion>7.2</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>SixLabors.ImageSharp.Drawing</AssemblyName>

2
src/ImageSharp/ColorSpaces/CieLab.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
/// <summary>
/// Represents a <see cref="CieLab"/> that has L, A, B values set to zero.
/// </summary>
public static readonly CieLab Empty = default(CieLab);
public static readonly CieLab Empty = default;
/// <summary>
/// The backing vector for SIMD support.

7
src/ImageSharp/ColorSpaces/CieLch.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLch)
{
return this.Equals((CieLch)obj);
}
return false;
return obj is CieLch other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLchuv.cs

@ -194,12 +194,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLchuv)
{
return this.Equals((CieLchuv)obj);
}
return false;
return obj is CieLchuv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieLuv.cs

@ -196,12 +196,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieLuv)
{
return this.Equals((CieLuv)obj);
}
return false;
return obj is CieLuv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyChromaticityCoordinates.cs

@ -132,12 +132,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyChromaticityCoordinates)
{
return this.Equals((CieXyChromaticityCoordinates)obj);
}
return false;
return obj is CieXyChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyy.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyy)
{
return this.Equals((CieXyy)obj);
}
return false;
return obj is CieXyy other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/CieXyz.cs

@ -148,12 +148,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is CieXyz)
{
return this.Equals((CieXyz)obj);
}
return false;
return obj is CieXyz other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Cmyk.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Cmyk)
{
return this.Equals((Cmyk)obj);
}
return false;
return obj is Cmyk other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CIeLchToCieLabConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieLch input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, c = input.C, hDegrees = input.H;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieLchConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLch Convert(CieLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
float l = input.L, a = input.A, b = input.B;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLabToCieXyzConverter.cs

@ -16,8 +16,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLab input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
float l = input.L, a = input.A, b = input.B;
float fy = (l + 16) / 116F;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLchuvToCieLuvConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLuv Convert(CieLchuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, c = input.C, hDegrees = input.H;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieLchuvConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLchuv Convert(CieLuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here:
// https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29
float l = input.L, a = input.U, b = input.V;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieLuvToCieXyzConverter.cs

@ -15,8 +15,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieXyz Convert(CieLuv input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html
float l = input.L, u = input.U, v = input.V;

2
src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyzToCieLabConverter.cs

@ -43,8 +43,6 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public CieLab Convert(CieXyz input)
{
DebugGuard.NotNull(input, nameof(input));
// Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Lab.html
float wx = this.LabWhitePoint.X, wy = this.LabWhitePoint.Y, wz = this.LabWhitePoint.Z;

9
src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// Represents the chromaticity coordinates of RGB primaries.
/// One of the specifiers of <see cref="IRgbWorkingSpace"/>.
/// </summary>
internal struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
internal readonly struct RgbPrimariesChromaticityCoordinates : IEquatable<RgbPrimariesChromaticityCoordinates>
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbPrimariesChromaticityCoordinates"/> struct.
@ -76,12 +76,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RgbPrimariesChromaticityCoordinates coordinates)
{
return this.Equals(coordinates);
}
return false;
return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Conversion/Implementation/RgbWorkingSpace.cs

@ -73,12 +73,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion.Implementation
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (obj is RgbWorkingSpace space)
{
return this.Equals(space);
}
return false;
return obj is RgbWorkingSpace other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Hsl.cs

@ -150,12 +150,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Hsl)
{
return this.Equals((Hsl)obj);
}
return false;
return obj is Hsl other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Hsv.cs

@ -202,12 +202,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Hsv)
{
return this.Equals((Hsv)obj);
}
return false;
return obj is Hsv other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/HunterLab.cs

@ -190,12 +190,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is HunterLab)
{
return this.Equals((HunterLab)obj);
}
return false;
return obj is HunterLab other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/LinearRgb.cs

@ -182,12 +182,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is LinearRgb)
{
return this.Equals((LinearRgb)obj);
}
return false;
return obj is LinearRgb other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Lms.cs

@ -149,12 +149,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Lms lms)
{
return this.Equals(lms);
}
return false;
return obj is Lms other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/Rgb.cs

@ -204,12 +204,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is Rgb)
{
return this.Equals((Rgb)obj);
}
return false;
return obj is Rgb other && this.Equals(other);
}
/// <inheritdoc/>

7
src/ImageSharp/ColorSpaces/YCbCr.cs

@ -152,12 +152,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (obj is YCbCr)
{
return this.Equals((YCbCr)obj);
}
return false;
return obj is YCbCr other && this.Equals(other);
}
/// <inheritdoc/>

23
src/ImageSharp/Common/Helpers/DebugGuard.cs

@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
// TODO: These should just call the guard equivalents
namespace SixLabors.ImageSharp
{
/// <summary>
@ -114,6 +115,28 @@ namespace SixLabors.ImageSharp
}
}
/// <summary>
/// Verifies that the specified value is greater than or equal to a minimum value and less than
/// or equal to a maximum value and throws an exception if it is not.
/// </summary>
/// <param name="value">The target value, which should be validated.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException">
/// <paramref name="value"/> is less than the minimum value of greater than the maximum value.
/// </exception>
[Conditional("DEBUG")]
public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue min, TValue max, string parameterName)
where TValue : IComparable<TValue>
{
if (value.CompareTo(min) < 0 || value.CompareTo(max) > 0)
{
throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than or equal to {min} and less than or equal to {max}.");
}
}
/// <summary>
/// Verifies, that the method parameter with specified target value is true
/// and throws an exception if it is found to be so.

4
src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs

@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// 32 bits per pixel. Each pixel consists of 4 bytes.
/// </summary>
Pixel32 = 4,
Pixel32 = 4
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpCompression.cs

@ -58,4 +58,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
PNG = 5
}
}
}

2
src/ImageSharp/Formats/Bmp/BmpConstants.cs

@ -20,4 +20,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public static readonly IEnumerable<string> FileExtensions = new[] { "bm", "bmp", "dip" };
}
}
}

4
src/ImageSharp/Formats/Bmp/BmpDecoder.cs

@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Decode<TPixel>(stream);
}
@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <inheritdoc/>
public IImageInfo Identify(Configuration configuration, Stream stream)
{
Guard.NotNull(stream, "stream");
Guard.NotNull(stream, nameof(stream));
return new BmpDecoderCore(configuration, this).Identify(stream);
}

16
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -224,7 +224,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
using (Buffer2D<byte> buffer = this.memoryManager.AllocateClean2D<byte>(width, height))
{
this.UncompressRle8(width, buffer.Span);
@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
var color = default(TPixel);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
using (IManagedByteBuffer buffer = this.memoryManager.AllocateManagedByteBuffer(stride))
{
for (int y = 0; y < height; y++)
{
@ -581,13 +581,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader
{
Type = BitConverter.ToInt16(data, 0),
FileSize = BitConverter.ToInt32(data, 2),
Reserved = BitConverter.ToInt32(data, 6),
Offset = BitConverter.ToInt32(data, 10)
};
this.fileHeader = new BmpFileHeader(
type: BitConverter.ToInt16(data, 0),
fileSize: BitConverter.ToInt32(data, 2),
reserved: BitConverter.ToInt32(data, 6),
offset: BitConverter.ToInt32(data, 10));
}
/// <summary>

4
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -28,4 +26,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
encoder.Encode(image, stream);
}
}
}
}

17
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -55,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
BmpInfoHeader infoHeader = new BmpInfoHeader
var infoHeader = new BmpInfoHeader
{
HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
Height = image.Height,
@ -69,12 +69,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ClrImportant = 0
};
BmpFileHeader fileHeader = new BmpFileHeader
{
Type = 19778, // BM
Offset = 54,
FileSize = 54 + infoHeader.ImageSize
};
var fileHeader = new BmpFileHeader(
type: 19778, // BM
offset: 54,
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
WriteHeader(writer, fileHeader);
this.WriteInfo(writer, infoHeader);
@ -92,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </param>
private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader)
private static void WriteHeader(EndianBinaryWriter writer, in BmpFileHeader fileHeader)
{
writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize);

28
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -13,35 +13,43 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
/// </remarks>
internal sealed class BmpFileHeader
internal readonly struct BmpFileHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.
/// </summary>
public const int Size = 14;
public BmpFileHeader(short type, int fileSize, int reserved, int offset)
{
this.Type = type;
this.FileSize = fileSize;
this.Reserved = reserved;
this.Offset = offset;
}
/// <summary>
/// Gets or sets the Bitmap identifier.
/// Gets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x4D
/// (Hex code points for B and M)
/// </summary>
public short Type { get; set; }
public short Type { get; }
/// <summary>
/// Gets or sets the size of the bitmap file in bytes.
/// Gets the size of the bitmap file in bytes.
/// </summary>
public int FileSize { get; set; }
public int FileSize { get; }
/// <summary>
/// Gets or sets any reserved data; actual value depends on the application
/// Gets any reserved data; actual value depends on the application
/// that creates the image.
/// </summary>
public int Reserved { get; set; }
public int Reserved { get; }
/// <summary>
/// Gets or sets the offset, i.e. starting address, of the byte where
/// Gets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found.
/// </summary>
public int Offset { get; set; }
public int Offset { get; }
}
}
}

14
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -5,6 +5,7 @@ using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
@ -410,13 +411,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
private void ReadFrameColors<TPixel>(ref Image<TPixel> image, ref ImageFrame<TPixel> previousFrame, Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
where TPixel : struct, IPixel<TPixel>
{
ref byte indicesRef = ref MemoryMarshal.GetReference(indices);
int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height;
ImageFrame<TPixel> prevFrame = null;
ImageFrame<TPixel> currentFrame = null;
ImageFrame<TPixel> imageFrame;
if (previousFrame == null)
@ -479,7 +479,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
writeY = interlaceY + descriptor.Top;
interlaceY += interlaceIncrement;
}
else
@ -487,14 +486,13 @@ namespace SixLabors.ImageSharp.Formats.Gif
writeY = y;
}
Span<TPixel> rowSpan = imageFrame.GetPixelRowSpan(writeY);
ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.GetPixelRowSpan(writeY));
var rgba = new Rgba32(0, 0, 0, 255);
// #403 The left + width value can be larger than the image width
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < rowSpan.Length; x++)
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width && x < imageWidth; x++)
{
int index = indices[i];
int index = Unsafe.Add(ref indicesRef, i);
if (this.graphicsControlExtension == null ||
this.graphicsControlExtension.TransparencyFlag == false ||
@ -502,7 +500,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int indexOffset = index * 3;
ref TPixel pixel = ref rowSpan[x];
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable.GetRgb24(indexOffset);
pixel.PackFromRgba32(rgba);

40
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -4,6 +4,8 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
@ -11,6 +13,9 @@ using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Quantization;
// TODO: This is causing more GC collections than I'm happy with.
// This is likely due to the number of short writes to the stream we are doing.
// We should investigate reducing them since we know the length of the byte array we require for multiple parts.
namespace SixLabors.ImageSharp.Formats.Gif
{
/// <summary>
@ -45,11 +50,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
private int bitDepth;
/// <summary>
/// Whether the current image has multiple frames.
/// </summary>
private bool hasFrames;
/// <summary>
/// Initializes a new instance of the <see cref="GifEncoderCore"/> class.
/// </summary>
@ -78,8 +78,6 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
this.hasFrames = image.Frames.Count > 1;
// Quantize the image returning a palette.
QuantizedFrame<TPixel> quantized = this.quantizer.CreateFrameQuantizer<TPixel>().QuantizeFrame(image.Frames.RootFrame);
@ -98,9 +96,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.WriteComments(image, writer);
// Write additional frames.
if (this.hasFrames)
if (image.Frames.Count > 1)
{
this.WriteApplicationExtension(writer, image.MetaData.RepeatCount, image.Frames.Count);
this.WriteApplicationExtension(writer, image.MetaData.RepeatCount);
}
foreach (ImageFrame<TPixel> frame in image.Frames)
@ -138,11 +136,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Transparent pixels are much more likely to be found at the end of a palette
int index = -1;
var trans = default(Rgba32);
ref TPixel paletteRef = ref MemoryMarshal.GetReference(quantized.Palette.AsSpan());
for (int i = quantized.Palette.Length - 1; i >= 0; i--)
{
quantized.Palette[i].ToRgba32(ref trans);
if (trans.Equals(default(Rgba32)))
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgba32(ref trans);
if (trans.Equals(default))
{
index = i;
}
@ -155,6 +154,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the file header signature and version to the stream.
/// </summary>
/// <param name="writer">The writer to write to the stream with.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteHeader(EndianBinaryWriter writer)
{
writer.Write(GifConstants.MagicNumber);
@ -201,11 +201,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="writer">The writer to write to the stream with.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="frames">The number of image frames.</param>
private void WriteApplicationExtension(EndianBinaryWriter writer, ushort repeatCount, int frames)
private void WriteApplicationExtension(EndianBinaryWriter writer, ushort repeatCount)
{
// Application Extension Header
if (repeatCount != 1 && frames > 0)
if (repeatCount != 1)
{
this.buffer[0] = GifConstants.ExtensionIntroducer;
this.buffer[1] = GifConstants.ApplicationExtensionLabel;
@ -336,15 +335,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
var rgb = default(Rgb24);
using (IManagedByteBuffer colorTable = this.memoryManager.AllocateManagedByteBuffer(colorTableLength))
{
Span<byte> colorTableSpan = colorTable.Span;
ref TPixel paletteRef = ref MemoryMarshal.GetReference(image.Palette.AsSpan());
ref Rgb24 rgb24Ref = ref Unsafe.As<byte, Rgb24>(ref MemoryMarshal.GetReference(colorTable.Span));
for (int i = 0; i < pixelCount; i++)
{
int offset = i * 3;
image.Palette[i].ToRgb24(ref rgb);
colorTableSpan[offset] = rgb.R;
colorTableSpan[offset + 1] = rgb.G;
colorTableSpan[offset + 2] = rgb.B;
ref TPixel entry = ref Unsafe.Add(ref paletteRef, i);
entry.ToRgb24(ref rgb);
Unsafe.Add(ref rgb24Ref, i) = rgb;
}
writer.Write(colorTable.Array, 0, colorTableLength);

37
src/ImageSharp/Formats/Gif/LzwDecoder.cs

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@ -115,14 +115,14 @@ namespace SixLabors.ImageSharp.Formats.Gif
int data = 0;
int first = 0;
Span<int> prefixSpan = this.prefix.Span;
Span<int> suffixSpan = this.suffix.Span;
Span<int> pixelStackSpan = this.pixelStack.Span;
ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.Span);
ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.Span);
ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.Span);
ref byte pixelsRef = ref MemoryMarshal.GetReference(pixels);
for (code = 0; code < clearCode; code++)
{
prefixSpan[code] = 0;
suffixSpan[code] = (byte)code;
Unsafe.Add(ref suffixRef, code) = (byte)code;
}
byte[] buffer = new byte[255];
@ -176,7 +176,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (oldCode == NullCode)
{
pixelStackSpan[top++] = suffixSpan[code];
Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code);
oldCode = code;
first = code;
continue;
@ -185,27 +185,27 @@ namespace SixLabors.ImageSharp.Formats.Gif
int inCode = code;
if (code == availableCode)
{
pixelStackSpan[top++] = (byte)first;
Unsafe.Add(ref pixelStackRef, top++) = (byte)first;
code = oldCode;
}
while (code > clearCode)
{
pixelStackSpan[top++] = suffixSpan[code];
code = prefixSpan[code];
Unsafe.Add(ref pixelStackRef, top++) = Unsafe.Add(ref suffixRef, code);
code = Unsafe.Add(ref prefixRef, code);
}
first = suffixSpan[code];
pixelStackSpan[top++] = suffixSpan[code];
int suffixCode = Unsafe.Add(ref suffixRef, code);
first = suffixCode;
Unsafe.Add(ref pixelStackRef, top++) = suffixCode;
// Fix for Gifs that have "deferred clear code" as per here :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if (availableCode < MaxStackSize)
{
prefixSpan[availableCode] = oldCode;
suffixSpan[availableCode] = first;
Unsafe.Add(ref prefixRef, availableCode) = oldCode;
Unsafe.Add(ref suffixRef, availableCode) = first;
availableCode++;
if (availableCode == codeMask + 1 && availableCode < MaxStackSize)
{
@ -221,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
top--;
// Clear missing pixels
pixels[xyz++] = (byte)pixelStackSpan[top];
Unsafe.Add(ref pixelsRef, xyz++) = (byte)Unsafe.Add(ref pixelStackRef, top);
}
}
@ -238,8 +238,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="buffer">The buffer to store the block in.</param>
/// <returns>
/// The <see cref="T:byte[]"/>.
/// The <see cref="int"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadBlock(byte[] buffer)
{
int bufferSize = this.stream.ReadByte();

62
src/ImageSharp/Formats/Gif/LzwEncoder.cs

@ -2,9 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Gif
@ -233,6 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <param name="bitCount">The number of bits</param>
/// <returns>See <see cref="int"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetMaxcode(int bitCount)
{
return (1 << bitCount) - 1;
@ -243,10 +244,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// flush the packet to disk.
/// </summary>
/// <param name="c">The character to add.</param>
/// <param name="accumulatorsRef">The reference to the storage for packat accumulators</param>
/// <param name="stream">The stream to write to.</param>
private void AddCharacter(byte c, Stream stream)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddCharacter(byte c, ref byte accumulatorsRef, Stream stream)
{
this.accumulators[this.accumulatorCount++] = c;
Unsafe.Add(ref accumulatorsRef, this.accumulatorCount++) = c;
if (this.accumulatorCount >= 254)
{
this.FlushPacket(stream);
@ -257,6 +260,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Table clear for block compress
/// </summary>
/// <param name="stream">The output stream.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearBlock(Stream stream)
{
this.ResetCodeTable();
@ -269,15 +273,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Reset the code table.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ResetCodeTable()
{
this.hashTable.Span.Fill(-1);
// Original code:
// for (int i = 0; i < size; ++i)
// {
// this.hashTable[i] = -1;
// }
}
/// <summary>
@ -309,6 +308,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = this.NextPixel();
// TODO: PERF: It looks likt hshift could be calculated once statically.
hshift = 0;
for (fcode = this.hsize; fcode < 65536; fcode *= 2)
{
@ -323,22 +323,22 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.Output(this.clearCode, stream);
Span<int> hashTableSpan = this.hashTable.Span;
Span<int> codeTableSpan = this.codeTable.Span;
ref int hashTableRef = ref MemoryMarshal.GetReference(this.hashTable.Span);
ref int codeTableRef = ref MemoryMarshal.GetReference(this.codeTable.Span);
while ((c = this.NextPixel()) != Eof)
{
fcode = (c << this.maxbits) + ent;
int i = (c << hshift) ^ ent /* = 0 */;
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
ent = codeTableSpan[i];
ent = Unsafe.Add(ref codeTableRef, i);
continue;
}
// Non-empty slot
if (hashTableSpan[i] >= 0)
if (Unsafe.Add(ref hashTableRef, i) >= 0)
{
int disp = hsizeReg - i;
if (i == 0)
@ -353,15 +353,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
i += hsizeReg;
}
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
ent = codeTableSpan[i];
ent = Unsafe.Add(ref codeTableRef, i);
break;
}
}
while (hashTableSpan[i] >= 0);
while (Unsafe.Add(ref hashTableRef, i) >= 0);
if (hashTableSpan[i] == fcode)
if (Unsafe.Add(ref hashTableRef, i) == fcode)
{
continue;
}
@ -371,8 +371,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
ent = c;
if (this.freeEntry < this.maxmaxcode)
{
codeTableSpan[i] = this.freeEntry++; // code -> hashtable
hashTableSpan[i] = fcode;
Unsafe.Add(ref codeTableRef, i) = this.freeEntry++; // code -> hashtable
Unsafe.Add(ref hashTableRef, i) = fcode;
}
else
{
@ -390,14 +390,12 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Flush the packet to disk, and reset the accumulator.
/// </summary>
/// <param name="outStream">The output stream.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void FlushPacket(Stream outStream)
{
if (this.accumulatorCount > 0)
{
outStream.WriteByte((byte)this.accumulatorCount);
outStream.Write(this.accumulators, 0, this.accumulatorCount);
this.accumulatorCount = 0;
}
outStream.WriteByte((byte)this.accumulatorCount);
outStream.Write(this.accumulators, 0, this.accumulatorCount);
this.accumulatorCount = 0;
}
/// <summary>
@ -424,6 +422,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="outs">The stream to write to.</param>
private void Output(int code, Stream outs)
{
ref byte accumulatorsRef = ref MemoryMarshal.GetReference(this.accumulators.AsSpan());
this.currentAccumulator &= Masks[this.currentBits];
if (this.currentBits > 0)
@ -439,7 +438,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
while (this.currentBits >= 8)
{
this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
this.AddCharacter((byte)(this.currentAccumulator & 0xFF), ref accumulatorsRef, outs);
this.currentAccumulator >>= 8;
this.currentBits -= 8;
}
@ -467,12 +466,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
// At EOF, write the rest of the buffer.
while (this.currentBits > 0)
{
this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
this.AddCharacter((byte)(this.currentAccumulator & 0xFF), ref accumulatorsRef, outs);
this.currentAccumulator >>= 8;
this.currentBits -= 8;
}
this.FlushPacket(outs);
if (this.accumulatorCount > 0)
{
this.FlushPacket(outs);
}
}
}

64
src/ImageSharp/Formats/Gif/PackedField.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Gif
{
@ -21,6 +23,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
public byte Byte
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
int returnValue = 0;
@ -53,7 +56,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <returns>The <see cref="PackedField"/></returns>
public static PackedField FromInt(byte value)
{
PackedField packed = default(PackedField);
PackedField packed = default;
packed.SetBits(0, 8, value);
return packed;
}
@ -70,12 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </param>
public void SetBit(int index, bool valueToSet)
{
if (index < 0 || index > 7)
{
string message = $"Index must be between 0 and 7. Supplied index: {index}";
throw new ArgumentOutOfRangeException(nameof(index), message);
}
DebugGuard.MustBeBetweenOrEqualTo(index, 0, 7, nameof(index));
Bits[index] = valueToSet;
}
@ -88,18 +86,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="valueToSet">The value to set the bits to.</param>
public void SetBits(int startIndex, int length, int valueToSet)
{
if (startIndex < 0 || startIndex > 7)
{
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(startIndex), message);
}
if (length < 1 || startIndex + length > 8)
{
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. "
+ $"Supplied length: {length}. Supplied start index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(length), message);
}
DebugGuard.MustBeBetweenOrEqualTo(startIndex, 0, 7, nameof(startIndex));
DebugCheckLength(startIndex, length);
int bitShift = length - 1;
for (int i = startIndex; i < startIndex + length; i++)
@ -121,12 +109,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </returns>
public bool GetBit(int index)
{
if (index < 0 || index > 7)
{
string message = $"Index must be between 0 and 7. Supplied index: {index}";
throw new ArgumentOutOfRangeException(nameof(index), message);
}
DebugGuard.MustBeBetweenOrEqualTo(index, 0, 7, nameof(index));
return Bits[index];
}
@ -140,19 +123,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </returns>
public int GetBits(int startIndex, int length)
{
if (startIndex < 0 || startIndex > 7)
{
string message = $"Start index must be between 0 and 7. Supplied index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(startIndex), message);
}
if (length < 1 || startIndex + length > 8)
{
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. "
+ $"Supplied length: {length}. Supplied start index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(length), message);
}
DebugGuard.MustBeBetweenOrEqualTo(startIndex, 1, 8, nameof(startIndex));
DebugCheckLength(startIndex, length);
int returnValue = 0;
int bitShift = length - 1;
@ -169,9 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <inheritdoc/>
public override bool Equals(object obj)
{
PackedField? field = obj as PackedField?;
return this.Byte == field?.Byte;
return obj is PackedField other && this.Equals(other);
}
/// <inheritdoc/>
@ -191,5 +161,17 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
return this.Byte.GetHashCode();
}
[Conditional("DEBUG")]
private static void DebugCheckLength(int startIndex, int length)
{
if (length < 1 || startIndex + length > 8)
{
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. "
+ $"Supplied length: {length}. Supplied start index: {startIndex}";
throw new ArgumentOutOfRangeException(nameof(length), message);
}
}
}
}

23
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public Block8x8(Span<short> coefficients)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte sourceRef = ref MemoryMarshal.GetReference(coefficients.NonPortableCast<short, byte>());
ref byte sourceRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<short, byte>(coefficients));
Unsafe.CopyBlock(ref selfRef, ref sourceRef, Size * sizeof(short));
}
@ -205,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
public void CopyTo(Span<short> destination)
{
ref byte selfRef = ref Unsafe.As<Block8x8, byte>(ref this);
ref byte destRef = ref MemoryMarshal.GetReference(destination.NonPortableCast<short, byte>());
ref byte destRef = ref MemoryMarshal.GetReference(MemoryMarshal.Cast<short, byte>(destination));
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
@ -241,19 +241,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
bld.Append('[');
var sb = new StringBuilder();
sb.Append('[');
for (int i = 0; i < Size; i++)
{
bld.Append(this[i]);
sb.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
sb.Append(',');
}
}
bld.Append(']');
return bld.ToString();
sb.Append(']');
return sb.ToString();
}
/// <inheritdoc />
@ -273,12 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is Block8x8 && this.Equals((Block8x8)obj);
return obj is Block8x8 other && this.Equals(other);
}
/// <inheritdoc />

12
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -496,19 +496,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <inheritdoc />
public override string ToString()
{
var bld = new StringBuilder();
bld.Append('[');
var sb = new StringBuilder();
sb.Append('[');
for (int i = 0; i < Size; i++)
{
bld.Append(this[i]);
sb.Append(this[i]);
if (i < Size - 1)
{
bld.Append(',');
sb.Append(',');
}
}
bld.Append(']');
return bld.ToString();
sb.Append(']');
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

7
src/ImageSharp/Formats/Jpeg/Common/Decoder/AdobeMarker.cs

@ -94,12 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is AdobeMarker && this.Equals((AdobeMarker)obj);
return obj is AdobeMarker other && this.Equals(other);
}
/// <inheritdoc/>

9
src/ImageSharp/Formats/Jpeg/Common/Decoder/JFifMarker.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
}
}
marker = default(JFifMarker);
marker = default;
return false;
}
@ -104,12 +104,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder
/// <inheritdoc/>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is JFifMarker && this.Equals((JFifMarker)obj);
return obj is JFifMarker other && this.Equals(other);
}
/// <inheritdoc/>

11
src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs

@ -427,10 +427,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
{
if (this.isExif)
{
ExifValue horizontal = this.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = this.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizonalTag)
? ((Rational)horizonalTag.Value).ToDouble()
: 0;
double verticalValue = this.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{

6
src/ImageSharp/Formats/Jpeg/PdfJsPort/Components/PdfJsFileMarker.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
/// <summary>
/// Represents a jpeg file marker
/// </summary>
internal struct PdfJsFileMarker
internal readonly struct PdfJsFileMarker
{
/// <summary>
/// Initializes a new instance of the <see cref="PdfJsFileMarker"/> struct.
@ -34,9 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components
}
/// <summary>
/// Gets or sets a value indicating whether the current marker is invalid
/// Gets a value indicating whether the current marker is invalid
/// </summary>
public bool Invalid { get; set; }
public bool Invalid { get; }
/// <summary>
/// Gets the position of the marker within a stream

11
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -380,10 +380,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
if (this.isExif)
{
ExifValue horizontal = image.MetaData.ExifProfile.GetValue(ExifTag.XResolution);
ExifValue vertical = image.MetaData.ExifProfile.GetValue(ExifTag.YResolution);
double horizontalValue = horizontal != null ? ((Rational)horizontal.Value).ToDouble() : 0;
double verticalValue = vertical != null ? ((Rational)vertical.Value).ToDouble() : 0;
double horizontalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.XResolution, out ExifValue horizontalTag)
? ((Rational)horizontalTag.Value).ToDouble()
: 0;
double verticalValue = image.MetaData.ExifProfile.TryGetValue(ExifTag.YResolution, out ExifValue verticalTag)
? ((Rational)verticalTag.Value).ToDouble()
: 0;
if (horizontalValue > 0 && verticalValue > 0)
{

9
src/ImageSharp/Formats/Png/PngChunk.cs

@ -10,13 +10,18 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
internal sealed class PngChunk
{
public PngChunk(int length)
{
this.Length = length;
}
/// <summary>
/// Gets or sets the length.
/// Gets the length.
/// An unsigned integer giving the number of bytes in the chunk's
/// data field. The length counts only the data field, not itself,
/// the chunk type code, or the CRC. Zero is a valid length
/// </summary>
public int Length { get; set; }
public int Length { get; }
/// <summary>
/// Gets or sets the chunk type as string with 4 chars.

97
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Png.Filters;
@ -217,19 +218,18 @@ namespace SixLabors.ImageSharp.Formats.Png
{
using (var deframeStream = new ZlibInflateStream(this.currentStream))
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
if (image == null)
@ -237,23 +237,23 @@ namespace SixLabors.ImageSharp.Formats.Png
this.InitializeImage(metadata, out image);
}
deframeStream.AllocateNewBytes(currentChunk.Length);
deframeStream.AllocateNewBytes(chunk.Length);
this.ReadScanlines(deframeStream.CompressedStream, image.Frames.RootFrame);
this.currentStream.Read(this.crcBuffer, 0, 4);
break;
case PngChunkTypes.Palette:
byte[] pal = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, pal, 0, currentChunk.Length);
byte[] pal = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, pal, 0, chunk.Length);
this.palette = pal;
break;
case PngChunkTypes.PaletteAlpha:
byte[] alpha = new byte[currentChunk.Length];
Buffer.BlockCopy(currentChunk.Data.Array, 0, alpha, 0, currentChunk.Length);
byte[] alpha = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, alpha, 0, chunk.Length);
this.paletteAlpha = alpha;
this.AssignTransparentMarkers(alpha);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -263,10 +263,10 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
if (chunk.Data != null)
{
currentChunk.Data.Dispose();
currentChunk.Data = null;
chunk.Data.Dispose();
chunk.Data = null;
}
}
}
@ -297,25 +297,24 @@ namespace SixLabors.ImageSharp.Formats.Png
this.currentStream.Skip(8);
try
{
PngChunk currentChunk;
while (!this.isEndChunkReached && (currentChunk = this.ReadChunk()) != null)
while (!this.isEndChunkReached && this.TryReadChunk(out PngChunk chunk))
{
try
{
switch (currentChunk.Type)
switch (chunk.Type)
{
case PngChunkTypes.Header:
this.ReadHeaderChunk(currentChunk.Data.Array);
this.ReadHeaderChunk(chunk.Data.Array);
this.ValidateHeader();
break;
case PngChunkTypes.Physical:
this.ReadPhysicalChunk(metadata, currentChunk.Data.Array);
this.ReadPhysicalChunk(metadata, chunk.Data.Array);
break;
case PngChunkTypes.Data:
this.SkipChunkDataAndCrc(currentChunk);
this.SkipChunkDataAndCrc(chunk);
break;
case PngChunkTypes.Text:
this.ReadTextChunk(metadata, currentChunk.Data.Array, currentChunk.Length);
this.ReadTextChunk(metadata, chunk.Data.Array, chunk.Length);
break;
case PngChunkTypes.End:
this.isEndChunkReached = true;
@ -325,9 +324,9 @@ namespace SixLabors.ImageSharp.Formats.Png
finally
{
// Data is rented in ReadChunkData()
if (currentChunk.Data != null)
if (chunk.Data != null)
{
ArrayPool<byte>.Shared.Return(currentChunk.Data.Array);
ArrayPool<byte>.Shared.Return(chunk.Data.Array);
}
}
}
@ -338,7 +337,7 @@ namespace SixLabors.ImageSharp.Formats.Png
this.previousScanline?.Dispose();
}
if (this.header == null)
if (this.header.Width == 0 && this.header.Height == 0)
{
throw new ImageFormatException("PNG Image does not contain a header chunk");
}
@ -778,7 +777,7 @@ namespace SixLabors.ImageSharp.Formats.Png
// TODO: Should we use pack from vector here instead?
this.From16BitTo8Bit(scanlineBuffer, compressed.Span, length);
Span<Rgb24> rgb24Span = compressed.Span.NonPortableCast<byte, Rgb24>();
Span<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(compressed.Span);
for (int x = 0; x < this.header.Width; x++)
{
ref Rgb24 rgb24 = ref rgb24Span[x];
@ -793,7 +792,7 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
ReadOnlySpan<Rgb24> rgb24Span = scanlineBuffer.NonPortableCast<byte, Rgb24>();
ReadOnlySpan<Rgb24> rgb24Span = MemoryMarshal.Cast<byte, Rgb24>(scanlineBuffer);
for (int x = 0; x < this.header.Width; x++)
{
ref readonly Rgb24 rgb24 = ref rgb24Span[x];
@ -1159,16 +1158,14 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="data">The <see cref="T:ReadOnlySpan{byte}"/> containing data.</param>
private void ReadHeaderChunk(ReadOnlySpan<byte> data)
{
this.header = new PngHeader
{
Width = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
Height = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
BitDepth = data[8],
ColorType = (PngColorType)data[9],
CompressionMethod = data[10],
FilterMethod = data[11],
InterlaceMethod = (PngInterlaceMode)data[12]
};
this.header = new PngHeader(
width: BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4)),
height: BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)),
bitDepth: data[8],
colorType: (PngColorType)data[9],
compressionMethod: data[10],
filterMethod: data[11],
interlaceMethod: (PngInterlaceMode)data[12]);
}
/// <summary>
@ -1208,36 +1205,40 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <returns>
/// The <see cref="PngChunk"/>.
/// </returns>
private PngChunk ReadChunk()
private bool TryReadChunk(out PngChunk chunk)
{
var chunk = new PngChunk();
this.ReadChunkLength(chunk);
int length = this.ReadChunkLength();
if (chunk.Length == -1)
if (length == -1)
{
chunk = default;
// IEND
return null;
return false;
}
chunk = new PngChunk(length);
if (chunk.Length < 0 || chunk.Length > this.currentStream.Length - this.currentStream.Position)
{
// Not a valid chunk so we skip back all but one of the four bytes we have just read.
// That lets us read one byte at a time until we reach a known chunk.
this.currentStream.Position -= 3;
return chunk;
return true;
}
this.ReadChunkType(chunk);
if (chunk.Type == PngChunkTypes.Data)
{
return chunk;
return true;
}
this.ReadChunkData(chunk);
this.ReadChunkCrc(chunk);
return chunk;
return true;
}
/// <summary>
@ -1314,21 +1315,19 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Calculates the length of the given chunk.
/// </summary>
/// <param name="chunk">The chunk.</param>
/// <exception cref="ImageFormatException">
/// Thrown if the input stream is not valid.
/// </exception>
private void ReadChunkLength(PngChunk chunk)
private int ReadChunkLength()
{
int numBytes = this.currentStream.Read(this.chunkLengthBuffer, 0, 4);
if (numBytes < 4)
{
chunk.Length = -1;
return;
return -1;
}
chunk.Length = BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
return BinaryPrimitives.ReadInt32BigEndian(this.chunkLengthBuffer);
}
/// <summary>

24
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -212,16 +212,14 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerPixel = this.CalculateBytesPerPixel();
var header = new PngHeader
{
Width = image.Width,
Height = image.Height,
ColorType = this.pngColorType,
BitDepth = this.bitDepth,
FilterMethod = 0, // None
CompressionMethod = 0,
InterlaceMethod = 0
};
var header = new PngHeader(
width: image.Width,
height: image.Height,
colorType: this.pngColorType,
bitDepth: this.bitDepth,
filterMethod: 0, // None
compressionMethod: 0,
interlaceMethod: 0);
this.WriteHeaderChunk(stream, header);
@ -415,7 +413,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// </summary>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
private void WriteHeaderChunk(Stream stream, PngHeader header)
private void WriteHeaderChunk(Stream stream, in PngHeader header)
{
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 0, 4), header.Width);
BinaryPrimitives.WriteInt32BigEndian(new Span<byte>(this.chunkDataBuffer, 4, 4), header.Height);
@ -436,7 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
/// <param name="header">The <see cref="PngHeader"/>.</param>
/// <param name="quantized">The quantized frame.</param>
private void WritePaletteChunk<TPixel>(Stream stream, PngHeader header, QuantizedFrame<TPixel> quantized)
private void WritePaletteChunk<TPixel>(Stream stream, in PngHeader header, QuantizedFrame<TPixel> quantized)
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
@ -561,7 +559,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
for (int y = 0; y < this.height; y++)
{
IManagedByteBuffer r = this.EncodePixelRow(pixels.GetPixelRowSpan(y).AsReadOnlySpan(), y);
IManagedByteBuffer r = this.EncodePixelRow((ReadOnlySpan<TPixel>)pixels.GetPixelRowSpan(y), y);
deflateStream.Write(r.Array, 0, resultLength);
IManagedByteBuffer temp = this.rawScanline;

48
src/ImageSharp/Formats/Png/PngHeader.cs

@ -6,55 +6,73 @@ namespace SixLabors.ImageSharp.Formats.Png
/// <summary>
/// Represents the png header chunk.
/// </summary>
internal sealed class PngHeader
internal readonly struct PngHeader
{
public PngHeader(
int width,
int height,
byte bitDepth,
PngColorType colorType,
byte compressionMethod,
byte filterMethod,
PngInterlaceMode interlaceMethod)
{
this.Width = width;
this.Height = height;
this.BitDepth = bitDepth;
this.ColorType = colorType;
this.CompressionMethod = compressionMethod;
this.FilterMethod = filterMethod;
this.InterlaceMethod = interlaceMethod;
}
/// <summary>
/// Gets or sets the dimension in x-direction of the image in pixels.
/// Gets the dimension in x-direction of the image in pixels.
/// </summary>
public int Width { get; set; }
public int Width { get; }
/// <summary>
/// Gets or sets the dimension in y-direction of the image in pixels.
/// Gets the dimension in y-direction of the image in pixels.
/// </summary>
public int Height { get; set; }
public int Height { get; }
/// <summary>
/// Gets or sets the bit depth.
/// Gets the bit depth.
/// Bit depth is a single-byte integer giving the number of bits per sample
/// or per palette index (not per pixel). Valid values are 1, 2, 4, 8, and 16,
/// although not all values are allowed for all color types.
/// </summary>
public byte BitDepth { get; set; }
public byte BitDepth { get; }
/// <summary>
/// Gets or sets the color type.
/// Gets the color type.
/// Color type is a integer that describes the interpretation of the
/// image data. Color type codes represent sums of the following values:
/// 1 (palette used), 2 (color used), and 4 (alpha channel used).
/// </summary>
public PngColorType ColorType { get; set; }
public PngColorType ColorType { get; }
/// <summary>
/// Gets or sets the compression method.
/// Gets the compression method.
/// Indicates the method used to compress the image data. At present,
/// only compression method 0 (deflate/inflate compression with a sliding
/// window of at most 32768 bytes) is defined.
/// </summary>
public byte CompressionMethod { get; set; }
public byte CompressionMethod { get; }
/// <summary>
/// Gets or sets the preprocessing method.
/// Gets the preprocessing method.
/// Indicates the preprocessing method applied to the image
/// data before compression. At present, only filter method 0
/// (adaptive filtering with five basic filter types) is defined.
/// </summary>
public byte FilterMethod { get; set; }
public byte FilterMethod { get; }
/// <summary>
/// Gets or sets the transmission order.
/// Gets the transmission order.
/// Indicates the transmission order of the image data.
/// Two values are currently defined: 0 (no interlace) or 1 (Adam7 interlace).
/// </summary>
public PngInterlaceMode InterlaceMethod { get; set; }
public PngInterlaceMode InterlaceMethod { get; }
}
}

1
src/ImageSharp/IImageFrameCollection.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.PixelFormats;

8
src/ImageSharp/Image.FromStream.cs

@ -183,15 +183,15 @@ namespace SixLabors.ImageSharp
return data.img;
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Image cannot be loaded. Available decoders:");
var sb = new StringBuilder();
sb.AppendLine("Image cannot be loaded. Available decoders:");
foreach (KeyValuePair<IImageFormat, IImageDecoder> val in config.ImageFormatsManager.ImageDecoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
private static T WithSeekableStream<T>(Configuration config, Stream stream, Func<Stream, T> action)

5
src/ImageSharp/Image.LoadPixelData.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> LoadPixelData<TPixel>(Configuration config, byte[] data, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(config, new Span<byte>(data).NonPortableCast<byte, TPixel>(), width, height);
=> LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(data.AsSpan()), width, height);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given byte array in <typeparamref name="TPixel"/> format.
@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
private static Image<TPixel> LoadPixelData<TPixel>(Configuration config, Span<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(config, data.NonPortableCast<byte, TPixel>(), width, height);
=> LoadPixelData(config, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.

33
src/ImageSharp/ImageExtensions.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
@ -35,28 +36,28 @@ namespace SixLabors.ImageSharp
IImageFormat format = source.GetConfiguration().ImageFormatsManager.FindFormatByFileExtension(ext);
if (format == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
var sb = new StringBuilder();
sb.AppendLine($"Can't find a format that is associated with the file extention '{ext}'. Registered formats with there extensions include:");
foreach (IImageFormat fmt in source.GetConfiguration().ImageFormats)
{
stringBuilder.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
sb.AppendLine($" - {fmt.Name} : {string.Join(", ", fmt.FileExtensions)}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format);
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
var sb = new StringBuilder();
sb.AppendLine($"Can't find encoder for file extention '{ext}' using image format '{format.Name}'. Registered encoders include:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> enc in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
sb.AppendLine($" - {enc.Key} : {enc.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
source.Save(filePath, encoder);
@ -97,22 +98,22 @@ namespace SixLabors.ImageSharp
if (encoder == null)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Can't find encoder for provided mime type. Available encoded:");
var sb = new StringBuilder();
sb.AppendLine("Can't find encoder for provided mime type. Available encoded:");
foreach (KeyValuePair<IImageFormat, IImageEncoder> val in source.GetConfiguration().ImageFormatsManager.ImageEncoders)
{
stringBuilder.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}");
}
throw new NotSupportedException(stringBuilder.ToString());
throw new NotSupportedException(sb.ToString());
}
source.Save(stream, encoder);
}
/// <summary>
/// Saves the raw image pixels to a byte array in row-major order.
/// Saves the raw image pixels to a byte array in row-major order.
/// </summary>
/// <typeparam name="TPixel">The Pixel format.</typeparam>
/// <param name="source">The source image</param>
@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source)
where TPixel : struct, IPixel<TPixel>
=> source.GetPixelSpan().AsBytes().ToArray();
=> MemoryMarshal.AsBytes(source.GetPixelSpan()).ToArray();
/// <summary>
/// Saves the raw image pixels to the given byte array in row-major order.
@ -131,7 +132,7 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer)
where TPixel : struct, IPixel<TPixel>
=> SavePixelData(source, buffer.AsSpan().NonPortableCast<byte, TPixel>());
=> SavePixelData(source, MemoryMarshal.Cast<byte, TPixel>(buffer.AsSpan()));
/// <summary>
/// Saves the raw image pixels to the given TPixel array in row-major order.
@ -205,7 +206,7 @@ namespace SixLabors.ImageSharp
/// <exception cref="System.ArgumentNullException">Thrown if the stream is null.</exception>
internal static void SavePixelData<TPixel>(this Image<TPixel> source, Span<byte> buffer)
where TPixel : struct, IPixel<TPixel>
=> source.Frames.RootFrame.SavePixelData(buffer.NonPortableCast<byte, TPixel>());
=> source.Frames.RootFrame.SavePixelData(MemoryMarshal.Cast<byte, TPixel>(buffer));
/// <summary>
/// Saves the raw image to the given bytes.

3
src/ImageSharp/ImageFrame.LoadPixelData.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
public static ImageFrame<TPixel> LoadPixelData<TPixel>(MemoryManager memoryManager, Span<byte> data, int width, int height)
where TPixel : struct, IPixel<TPixel>
=> LoadPixelData(memoryManager, data.NonPortableCast<byte, TPixel>(), width, height);
=> LoadPixelData(memoryManager, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
/// <summary>
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.

2
src/ImageSharp/ImageFrameCollection.cs

@ -77,6 +77,8 @@ namespace SixLabors.ImageSharp
/// <inheritdoc/>
public ImageFrame<TPixel> AddFrame(TPixel[] data)
{
Guard.NotNull(data, nameof(data));
var frame = ImageFrame.LoadPixelData(
this.parent.GetMemoryManager(),
new Span<TPixel>(data),

34
src/ImageSharp/ImageFrame{TPixel}.cs

@ -246,27 +246,23 @@ namespace SixLabors.ImageSharp
return this.Clone() as ImageFrame<TPixel2>;
}
Func<Vector4, Vector4> scaleFunc = PackedPixelConverterHelper.ComputeScaleFunction<TPixel, TPixel2>();
var target = new ImageFrame<TPixel2>(this.MemoryManager, this.Width, this.Height, this.MetaData.Clone());
using (PixelAccessor<TPixel> pixels = this.Lock())
using (PixelAccessor<TPixel2> targetPixels = target.Lock())
{
Parallel.For(
0,
target.Height,
Configuration.Default.ParallelOptions,
y =>
{
for (int x = 0; x < target.Width; x++)
{
var color = default(TPixel2);
color.PackFromVector4(scaleFunc(pixels[x, y].ToVector4()));
targetPixels[x, y] = color;
}
});
}
// TODO: ImageFrame has no visibility of the current configuration. It should have.
ParallelFor.WithTemporaryBuffer(
0,
this.Height,
Configuration.Default,
this.Width,
(int y, IBuffer<Vector4> tempRowBuffer) =>
{
Span<TPixel> sourceRow = this.GetPixelRowSpan(y);
Span<TPixel2> targetRow = target.GetPixelRowSpan(y);
Span<Vector4> tempRowSpan = tempRowBuffer.Span;
PixelOperations<TPixel>.Instance.ToScaledVector4(sourceRow, tempRowSpan, sourceRow.Length);
PixelOperations<TPixel2>.Instance.PackFromScaledVector4(tempRowSpan, targetRow, targetRow.Length);
});
return target;
}

4
src/ImageSharp/ImageSharp.csproj

@ -41,8 +41,8 @@
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Buffers" Version="4.4.0" />
<PackageReference Include="System.Memory" Version="4.5.0-preview1-26216-02" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0-preview1-26216-02" />
<PackageReference Include="System.Memory" Version="4.5.0-preview2-26406-04" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.0-preview2-26406-04" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' OR '$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />

3
src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Memory
{
@ -44,7 +45,7 @@ namespace SixLabors.ImageSharp.Memory
protected byte[] Data { get; private set; }
/// <inheritdoc />
public Span<T> Span => this.Data.AsSpan().NonPortableCast<byte, T>().Slice(0, this.length);
public Span<T> Span => MemoryMarshal.Cast<byte, T>(this.Data.AsSpan()).Slice(0, this.length);
/// <inheritdoc />
public void Dispose()

16
src/ImageSharp/Memory/SpanHelper.cs

@ -11,20 +11,6 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
internal static class SpanHelper
{
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The <see cref="Span{T}"/> to copy elements from.</param>
/// <param name="destination">The destination <see cref="Span{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(ReadOnlySpan<T> source, Span<T> destination, int count)
where T : struct
{
source.Slice(0, count).CopyTo(destination);
}
/// <summary>
/// Copy all elements of 'source' into 'destination'.
/// </summary>
@ -35,7 +21,7 @@ namespace SixLabors.ImageSharp.Memory
public static void Copy<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct
{
Copy(source, destination, Math.Min(source.Length, destination.Length));
source.Slice(0, Math.Min(source.Length, destination.Length)).CopyTo(destination);
}
/// <summary>

25
src/ImageSharp/MetaData/ImageProperty.cs

@ -10,10 +10,10 @@ namespace SixLabors.ImageSharp.MetaData
/// the copyright information, the date, where the image was created
/// or some other information.
/// </summary>
public class ImageProperty : IEquatable<ImageProperty>
public readonly struct ImageProperty : IEquatable<ImageProperty>
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class.
/// Initializes a new instance of the <see cref="ImageProperty"/> struct.
/// </summary>
/// <param name="name">The name of the property.</param>
/// <param name="value">The value of the property.</param>
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.MetaData
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageProperty"/> class
/// Initializes a new instance of the <see cref="ImageProperty"/> struct
/// by making a copy from another property.
/// </summary>
/// <param name="other">
@ -71,11 +71,6 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public static bool operator ==(ImageProperty left, ImageProperty right)
{
if (ReferenceEquals(left, right))
{
return true;
}
return left.Equals(right);
}
@ -110,9 +105,7 @@ namespace SixLabors.ImageSharp.MetaData
/// </returns>
public override bool Equals(object obj)
{
ImageProperty other = obj as ImageProperty;
return this.Equals(other);
return obj is ImageProperty other && this.Equals(other);
}
/// <summary>
@ -155,16 +148,6 @@ namespace SixLabors.ImageSharp.MetaData
/// <param name="other">An object to compare with this object.</param>
public bool Equals(ImageProperty other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return this.Name.Equals(other.Name) && Equals(this.Value, other.Value);
}
}

50
src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs

@ -11,66 +11,66 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Unknown
/// </summary>
Unknown,
Unknown = 0,
/// <summary>
/// Byte
/// An 8-bit unsigned integer.
/// </summary>
Byte,
Byte = 1,
/// <summary>
/// Ascii
/// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
/// </summary>
Ascii,
Ascii = 2,
/// <summary>
/// Short
/// A 16-bit (2-byte) unsigned integer.
/// </summary>
Short,
Short = 3,
/// <summary>
/// Long
/// A 32-bit (4-byte) unsigned integer.
/// </summary>
Long,
Long = 4,
/// <summary>
/// Rational
/// Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
/// </summary>
Rational,
Rational = 5,
/// <summary>
/// SignedByte
/// An 8-bit signed integer.
/// </summary>
SignedByte,
SignedByte = 6,
/// <summary>
/// Undefined
/// An 8-bit byte that can take any value depending on the field definition.
/// </summary>
Undefined,
Undefined = 7,
/// <summary>
/// SignedShort
/// A 16-bit (2-byte) signed integer.
/// </summary>
SignedShort,
SignedShort = 8,
/// <summary>
/// SignedLong
/// A 32-bit (4-byte) signed integer (2's complement notation).
/// </summary>
SignedLong,
SignedLong = 9,
/// <summary>
/// SignedRational
/// Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
/// </summary>
SignedRational,
SignedRational = 10,
/// <summary>
/// SingleFloat
/// A 32-bit floating point value.
/// </summary>
SingleFloat,
SingleFloat = 11,
/// <summary>
/// DoubleFloat
/// A 64-bit floating point value.
/// </summary>
DoubleFloat
DoubleFloat = 12
}
}

60
src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs

@ -23,12 +23,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// The collection of EXIF values
/// </summary>
private Collection<ExifValue> values;
private List<ExifValue> values;
/// <summary>
/// The list of invalid EXIF tags
/// </summary>
private List<ExifTag> invalidTags;
private IReadOnlyList<ExifTag> invalidTags;
/// <summary>
/// The thumbnail offset position in the byte stream
@ -75,8 +75,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
this.invalidTags = new List<ExifTag>(other.invalidTags);
if (other.values != null)
{
this.values = new Collection<ExifValue>();
foreach (ExifValue value in other.values)
this.values = new List<ExifValue>(other.Values.Count);
foreach (ExifValue value in other.Values)
{
this.values.Add(new ExifValue(value));
}
@ -92,21 +93,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <summary>
/// Gets or sets which parts will be written when the profile is added to an image.
/// </summary>
public ExifParts Parts
{
get;
set;
}
public ExifParts Parts { get; set; }
/// <summary>
/// Gets the tags that where found but contained an invalid value.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the values of this EXIF profile.
/// </summary>
public IEnumerable<ExifValue> Values
public IReadOnlyList<ExifValue> Values
{
get
{
@ -163,6 +160,31 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null;
}
/// <summary>
/// Conditionally returns the value of the tag if it exists.
/// </summary>
/// <param name="tag">The tag of the EXIF value.</param>
/// <param name="value">The value of the tag, if found.</param>
/// <returns>
/// The <see cref="bool"/>.
/// </returns>
public bool TryGetValue(ExifTag tag, out ExifValue value)
{
foreach (ExifValue exifValue in this.Values)
{
if (exifValue.Tag == tag)
{
value = exifValue;
return true;
}
}
value = default;
return false;
}
/// <summary>
/// Removes the value with the specified tag.
/// </summary>
@ -193,16 +215,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
public void SetValue(ExifTag tag, object value)
{
foreach (ExifValue exifValue in this.Values)
for (int i = 0; i < this.Values.Count; i++)
{
if (exifValue.Tag == tag)
if (this.values[i].Tag == tag)
{
exifValue.Value = value;
this.values[i] = this.values[i].WithValue(value);
return;
}
}
var newExifValue = ExifValue.Create(tag, value);
this.values.Add(newExifValue);
}
@ -262,12 +286,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.data == null)
{
this.values = new Collection<ExifValue>();
this.values = new List<ExifValue>();
return;
}
var reader = new ExifReader();
this.values = reader.Read(this.data);
var reader = new ExifReader(this.data);
this.values = reader.ReadValues();
this.invalidTags = new List<ExifTag>(reader.InvalidTags);
this.thumbnailOffset = (int)reader.ThumbnailOffset;
this.thumbnailLength = (int)reader.ThumbnailLength;

394
src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs

@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Primitives;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
@ -15,38 +18,37 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
internal sealed class ExifReader
{
private readonly Collection<ExifTag> invalidTags = new Collection<ExifTag>();
private byte[] exifData;
private uint currentIndex;
private bool isLittleEndian;
private readonly List<ExifTag> invalidTags = new List<ExifTag>();
private readonly byte[] exifData;
private int position;
private Endianness endianness = Endianness.BigEndian;
private uint exifOffset;
private uint gpsOffset;
private uint startIndex;
private int startIndex;
private delegate TDataType ConverterMethod<TDataType>(byte[] data);
public ExifReader(byte[] exifData)
{
DebugGuard.NotNull(exifData, nameof(exifData));
this.exifData = exifData;
}
private delegate TDataType ConverterMethod<TDataType>(ReadOnlySpan<byte> data);
/// <summary>
/// Gets the invalid tags.
/// </summary>
public IEnumerable<ExifTag> InvalidTags => this.invalidTags;
public IReadOnlyList<ExifTag> InvalidTags => this.invalidTags;
/// <summary>
/// Gets the thumbnail length in the byte stream
/// </summary>
public uint ThumbnailLength
{
get;
private set;
}
public uint ThumbnailLength { get; private set; }
/// <summary>
/// Gets the thumbnail offset position in the byte stream
/// </summary>
public uint ThumbnailOffset
{
get;
private set;
}
public uint ThumbnailOffset { get; private set; }
/// <summary>
/// Gets the remaining length.
@ -55,81 +57,78 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.currentIndex >= this.exifData.Length)
if (this.position >= this.exifData.Length)
{
return 0;
}
return this.exifData.Length - (int)this.currentIndex;
return this.exifData.Length - this.position;
}
}
/// <summary>
/// Reads and returns the collection of EXIF values.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>
/// The <see cref="Collection{ExifValue}"/>.
/// </returns>
public Collection<ExifValue> Read(byte[] data)
public List<ExifValue> ReadValues()
{
DebugGuard.NotNull(data, nameof(data));
var result = new Collection<ExifValue>();
var values = new List<ExifValue>();
this.exifData = data;
if (this.GetString(4) == "Exif")
if (this.ReadString(4) == "Exif")
{
if (this.GetShort() != 0)
if (this.ReadUInt16() != 0)
{
return result;
return values;
}
this.startIndex = 6;
}
else
{
this.currentIndex = 0;
this.position = 0;
}
this.isLittleEndian = this.GetString(2) == "II";
if (this.ReadString(2) == "II")
{
this.endianness = Endianness.LittleEndian;
}
if (this.GetShort() != 0x002A)
if (this.ReadUInt16() != 0x002A)
{
return result;
return values;
}
uint ifdOffset = this.GetLong();
this.AddValues(result, ifdOffset);
uint ifdOffset = this.ReadUInt32();
this.AddValues(values, (int)ifdOffset);
uint thumbnailOffset = this.GetLong();
this.GetThumbnail(thumbnailOffset);
uint thumbnailOffset = this.ReadUInt32();
this.GetThumbnail((int)thumbnailOffset);
if (this.exifOffset != 0)
{
this.AddValues(result, this.exifOffset);
this.AddValues(values, (int)this.exifOffset);
}
if (this.gpsOffset != 0)
{
this.AddValues(result, this.gpsOffset);
this.AddValues(values, (int)this.gpsOffset);
}
return result;
return values;
}
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, byte[] data, ConverterMethod<TDataType> converter)
private static TDataType[] ToArray<TDataType>(ExifDataType dataType, ReadOnlySpan<byte> data, ConverterMethod<TDataType> converter)
{
int dataTypeSize = (int)ExifValue.GetSize(dataType);
int length = data.Length / dataTypeSize;
TDataType[] result = new TDataType[length];
byte[] buffer = new byte[dataTypeSize];
var result = new TDataType[length];
for (int i = 0; i < length; i++)
{
Array.Copy(data, i * dataTypeSize, buffer, 0, dataTypeSize);
ReadOnlySpan<byte> buffer = data.Slice(i * dataTypeSize, dataTypeSize);
result.SetValue(converter(buffer), i);
}
@ -137,14 +136,23 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private static byte ToByte(byte[] data)
{
return data[0];
}
private byte ConvertToByte(ReadOnlySpan<byte> buffer) => buffer[0];
private static string ToString(byte[] data)
private unsafe string ConvertToString(ReadOnlySpan<byte> buffer)
{
string result = Encoding.UTF8.GetString(data, 0, data.Length);
#if NETSTANDARD1_1
byte[] bytes = buffer.ToArray();
string result = Encoding.UTF8.GetString(bytes, 0, buffer.Length);
#else
string result;
fixed (byte* pointer = &MemoryMarshal.GetReference(buffer))
{
result = Encoding.UTF8.GetString(pointer, buffer.Length);
}
#endif
int nullCharIndex = result.IndexOf('\0');
if (nullCharIndex != -1)
{
@ -159,15 +167,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
/// <param name="values">The values.</param>
/// <param name="index">The index.</param>
private void AddValues(Collection<ExifValue> values, uint index)
private void AddValues(List<ExifValue> values, int index)
{
this.currentIndex = this.startIndex + index;
ushort count = this.GetShort();
this.position = this.startIndex + index;
int count = this.ReadUInt16();
for (ushort i = 0; i < count; i++)
for (int i = 0; i < count; i++)
{
ExifValue value = this.CreateValue();
if (value == null)
if (!this.TryReadValue(out ExifValue value))
{
continue;
}
@ -208,9 +215,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private object ConvertValue(ExifDataType dataType, byte[] data, uint numberOfComponents)
private object ConvertValue(ExifDataType dataType, ReadOnlySpan<byte> buffer, uint numberOfComponents)
{
if (data == null || data.Length == 0)
if (buffer == null || buffer.Length == 0)
{
return null;
}
@ -220,106 +227,116 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifDataType.Unknown:
return null;
case ExifDataType.Ascii:
return ToString(data);
return this.ConvertToString(buffer);
case ExifDataType.Byte:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
case ExifDataType.DoubleFloat:
if (numberOfComponents == 1)
{
return this.ToDouble(data);
return this.ConvertToDouble(buffer);
}
return ToArray(dataType, data, this.ToDouble);
return ToArray(dataType, buffer, this.ConvertToDouble);
case ExifDataType.Long:
if (numberOfComponents == 1)
{
return this.ToLong(data);
return this.ConvertToUInt32(buffer);
}
return ToArray(dataType, data, this.ToLong);
return ToArray(dataType, buffer, this.ConvertToUInt32);
case ExifDataType.Rational:
if (numberOfComponents == 1)
{
return this.ToRational(data);
return this.ToRational(buffer);
}
return ToArray(dataType, data, this.ToRational);
return ToArray(dataType, buffer, this.ToRational);
case ExifDataType.Short:
if (numberOfComponents == 1)
{
return this.ToShort(data);
return this.ConvertToShort(buffer);
}
return ToArray(dataType, data, this.ToShort);
return ToArray(dataType, buffer, this.ConvertToShort);
case ExifDataType.SignedByte:
if (numberOfComponents == 1)
{
return this.ToSignedByte(data);
return this.ConvertToSignedByte(buffer);
}
return ToArray(dataType, data, this.ToSignedByte);
return ToArray(dataType, buffer, this.ConvertToSignedByte);
case ExifDataType.SignedLong:
if (numberOfComponents == 1)
{
return this.ToSignedLong(data);
return this.ConvertToInt32(buffer);
}
return ToArray(dataType, data, this.ToSignedLong);
return ToArray(dataType, buffer, this.ConvertToInt32);
case ExifDataType.SignedRational:
if (numberOfComponents == 1)
{
return this.ToSignedRational(data);
return this.ToSignedRational(buffer);
}
return ToArray(dataType, data, this.ToSignedRational);
return ToArray(dataType, buffer, this.ToSignedRational);
case ExifDataType.SignedShort:
if (numberOfComponents == 1)
{
return this.ToSignedShort(data);
return this.ConvertToSignedShort(buffer);
}
return ToArray(dataType, data, this.ToSignedShort);
return ToArray(dataType, buffer, this.ConvertToSignedShort);
case ExifDataType.SingleFloat:
if (numberOfComponents == 1)
{
return this.ToSingle(data);
return this.ConvertToSingle(buffer);
}
return ToArray(dataType, data, this.ToSingle);
return ToArray(dataType, buffer, this.ConvertToSingle);
case ExifDataType.Undefined:
if (numberOfComponents == 1)
{
return ToByte(data);
return this.ConvertToByte(buffer);
}
return data;
return buffer.ToArray();
default:
throw new NotSupportedException();
}
}
private ExifValue CreateValue()
private bool TryReadValue(out ExifValue exifValue)
{
// 2 | 2 | 4 | 4
// tag | type | count | value offset
if (this.RemainingLength < 12)
{
return null;
exifValue = default;
return false;
}
ExifTag tag = this.ToEnum(this.GetShort(), ExifTag.Unknown);
ExifDataType dataType = this.ToEnum(this.GetShort(), ExifDataType.Unknown);
object value;
ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
uint type = this.ReadUInt16();
if (dataType == ExifDataType.Unknown)
// Ensure that the data type is valid
if (type == 0 || type > 12)
{
return new ExifValue(tag, dataType, null, false);
exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false);
return true;
}
uint numberOfComponents = this.GetLong();
var dataType = (ExifDataType)type;
object value;
uint numberOfComponents = this.ReadUInt32();
// Issue #132: ExifDataType == Undefined is treated like a byte array.
// If numberOfComponents == 0 this value can only be handled as an inline value and must fallback to 4 (bytes)
@ -329,29 +346,50 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
uint size = numberOfComponents * ExifValue.GetSize(dataType);
byte[] data = this.GetBytes(4);
this.TryReadSpan(4, out ReadOnlySpan<byte> offsetBuffer);
if (size > 4)
{
uint oldIndex = this.currentIndex;
this.currentIndex = this.ToLong(data) + this.startIndex;
int oldIndex = this.position;
uint newIndex = this.ConvertToUInt32(offsetBuffer) + (uint)this.startIndex;
// Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue)
{
this.invalidTags.Add(tag);
exifValue = default;
return false;
}
this.position = (int)newIndex;
if (this.RemainingLength < size)
{
this.invalidTags.Add(tag);
this.currentIndex = oldIndex;
return null;
this.position = oldIndex;
exifValue = default;
return false;
}
value = this.ConvertValue(dataType, this.GetBytes(size), numberOfComponents);
this.currentIndex = oldIndex;
this.TryReadSpan((int)size, out ReadOnlySpan<byte> dataBuffer);
value = this.ConvertValue(dataType, dataBuffer, numberOfComponents);
this.position = oldIndex;
}
else
{
value = this.ConvertValue(dataType, data, numberOfComponents);
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
}
bool isArray = value != null && numberOfComponents > 1;
return new ExifValue(tag, dataType, value, isArray);
exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents > 1);
return true;
}
private TEnum ToEnum<TEnum>(int value, TEnum defaultValue)
@ -366,51 +404,57 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return defaultValue;
}
private byte[] GetBytes(uint length)
private bool TryReadSpan(int length, out ReadOnlySpan<byte> span)
{
if (this.currentIndex + length > (uint)this.exifData.Length)
if (this.RemainingLength < length)
{
return null;
span = default;
return false;
}
byte[] data = new byte[length];
Array.Copy(this.exifData, (int)this.currentIndex, data, 0, (int)length);
this.currentIndex += length;
span = new ReadOnlySpan<byte>(this.exifData, this.position, length);
return data;
this.position += length;
return true;
}
private uint GetLong()
private uint ReadUInt32()
{
return this.ToLong(this.GetBytes(4));
// Known as Long in Exif Specification
return this.TryReadSpan(4, out ReadOnlySpan<byte> span)
? this.ConvertToUInt32(span)
: default;
}
private ushort GetShort()
private ushort ReadUInt16()
{
return this.ToShort(this.GetBytes(2));
return this.TryReadSpan(2, out ReadOnlySpan<byte> span)
? this.ConvertToShort(span)
: default;
}
private string GetString(uint length)
private string ReadString(int length)
{
byte[] data = this.GetBytes(length);
if (data == null || data.Length == 0)
if (this.TryReadSpan(length, out ReadOnlySpan<byte> span) && span.Length != 0)
{
return null;
return this.ConvertToString(span);
}
return ToString(data);
return null;
}
private void GetThumbnail(uint offset)
private void GetThumbnail(int offset)
{
var values = new Collection<ExifValue>();
var values = new List<ExifValue>();
this.AddValues(values, offset);
foreach (ExifValue value in values)
{
if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
{
this.ThumbnailOffset = (uint)value.Value + this.startIndex;
this.ThumbnailOffset = (uint)value.Value + (uint)this.startIndex;
}
else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
{
@ -419,120 +463,112 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
}
private double ToDouble(byte[] data)
private unsafe double ConvertToDouble(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8))
if (buffer.Length < 8)
{
return default(double);
return default;
}
return BitConverter.ToDouble(data, 0);
long intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt64BigEndian(buffer)
: BinaryPrimitives.ReadInt64LittleEndian(buffer);
return *((double*)&intValue);
}
private uint ToLong(byte[] data)
private uint ConvertToUInt32(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 4))
// Known as Long in Exif Specification
if (buffer.Length < 4)
{
return default(uint);
return default;
}
return BitConverter.ToUInt32(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt32BigEndian(buffer)
: BinaryPrimitives.ReadUInt32LittleEndian(buffer);
}
private ushort ToShort(byte[] data)
private ushort ConvertToShort(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 2))
if (buffer.Length < 2)
{
return default(ushort);
return default;
}
return BitConverter.ToUInt16(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadUInt16BigEndian(buffer)
: BinaryPrimitives.ReadUInt16LittleEndian(buffer);
}
private float ToSingle(byte[] data)
private unsafe float ConvertToSingle(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 4))
if (buffer.Length < 4)
{
return default(float);
return default;
}
return BitConverter.ToSingle(data, 0);
int intValue = this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
return *((float*)&intValue);
}
private Rational ToRational(byte[] data)
private Rational ToRational(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8, 4))
if (buffer.Length < 8)
{
return default(Rational);
return default;
}
uint numerator = BitConverter.ToUInt32(data, 0);
uint denominator = BitConverter.ToUInt32(data, 4);
uint numerator = this.ConvertToUInt32(buffer.Slice(0, 4));
uint denominator = this.ConvertToUInt32(buffer.Slice(4, 4));
return new Rational(numerator, denominator, false);
}
private sbyte ToSignedByte(byte[] data)
private sbyte ConvertToSignedByte(ReadOnlySpan<byte> buffer)
{
return unchecked((sbyte)data[0]);
return unchecked((sbyte)buffer[0]);
}
private int ToSignedLong(byte[] data)
private int ConvertToInt32(ReadOnlySpan<byte> buffer) // SignedLong in Exif Specification
{
if (!this.ValidateArray(data, 4))
if (buffer.Length < 4)
{
return default(int);
return default;
}
return BitConverter.ToInt32(data, 0);
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt32BigEndian(buffer)
: BinaryPrimitives.ReadInt32LittleEndian(buffer);
}
private SignedRational ToSignedRational(byte[] data)
private SignedRational ToSignedRational(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 8, 4))
if (buffer.Length < 8)
{
return default(SignedRational);
return default;
}
int numerator = BitConverter.ToInt32(data, 0);
int denominator = BitConverter.ToInt32(data, 4);
int numerator = this.ConvertToInt32(buffer.Slice(0, 4));
int denominator = this.ConvertToInt32(buffer.Slice(4, 4));
return new SignedRational(numerator, denominator, false);
}
private short ToSignedShort(byte[] data)
private short ConvertToSignedShort(ReadOnlySpan<byte> buffer)
{
if (!this.ValidateArray(data, 2))
if (buffer.Length < 2)
{
return default(short);
return default;
}
return BitConverter.ToInt16(data, 0);
}
private bool ValidateArray(byte[] data, int size)
{
return this.ValidateArray(data, size, size);
}
private bool ValidateArray(byte[] data, int size, int stepSize)
{
if (data == null || data.Length < size)
{
return false;
}
if (this.isLittleEndian == BitConverter.IsLittleEndian)
{
return true;
}
for (int i = 0; i < data.Length; i += stepSize)
{
Array.Reverse(data, i, stepSize);
}
return true;
return this.endianness == Endianness.BigEndian
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
}
}

281
src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs

@ -0,0 +1,281 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using static SixLabors.ImageSharp.MetaData.Profiles.Exif.ExifTag;
namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
internal static class ExifTags
{
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
public static readonly ExifTag[] Ifd =
{
SubfileType,
OldSubfileType,
ImageWidth,
ImageLength,
BitsPerSample,
Compression,
PhotometricInterpretation,
Thresholding,
CellWidth,
CellLength,
FillOrder,
DocumentName,
ImageDescription,
Make,
Model,
StripOffsets,
Orientation,
SamplesPerPixel,
RowsPerStrip,
StripByteCounts,
MinSampleValue,
MaxSampleValue,
XResolution,
YResolution,
PlanarConfiguration,
PageName,
XPosition,
YPosition,
FreeOffsets,
FreeByteCounts,
GrayResponseUnit,
GrayResponseCurve,
T4Options,
T6Options,
ResolutionUnit,
PageNumber,
ColorResponseUnit,
TransferFunction,
Software,
DateTime,
Artist,
HostComputer,
Predictor,
WhitePoint,
PrimaryChromaticities,
ColorMap,
HalftoneHints,
TileWidth,
TileLength,
TileOffsets,
TileByteCounts,
BadFaxLines,
CleanFaxData,
ConsecutiveBadFaxLines,
InkSet,
InkNames,
NumberOfInks,
DotRange,
TargetPrinter,
ExtraSamples,
SampleFormat,
SMinSampleValue,
SMaxSampleValue,
TransferRange,
ClipPath,
XClipPathUnits,
YClipPathUnits,
Indexed,
JPEGTables,
OPIProxy,
ProfileType,
FaxProfile,
CodingMethods,
VersionYear,
ModeNumber,
Decode,
DefaultImageColor,
T82ptions,
JPEGProc,
JPEGInterchangeFormat,
JPEGInterchangeFormatLength,
JPEGRestartInterval,
JPEGLosslessPredictors,
JPEGPointTransforms,
JPEGQTables,
JPEGDCTables,
JPEGACTables,
YCbCrCoefficients,
YCbCrSubsampling,
YCbCrSubsampling,
YCbCrPositioning,
ReferenceBlackWhite,
StripRowCounts,
XMP,
Rating,
RatingPercent,
ImageID,
CFARepeatPatternDim,
CFAPattern2,
BatteryLevel,
Copyright,
MDFileTag,
MDScalePixel,
MDLabName,
MDSampleInfo,
MDPrepDate,
MDPrepTime,
MDFileUnits,
PixelScale,
IntergraphPacketData,
IntergraphRegisters,
IntergraphMatrix,
ModelTiePoint,
SEMInfo,
ModelTransform,
ImageLayer,
FaxRecvParams,
FaxSubaddress,
FaxRecvTime,
ImageSourceData,
XPTitle,
XPComment,
XPAuthor,
XPKeywords,
XPSubject,
GDALMetadata,
GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
public static readonly ExifTag[] Exif =
{
ExposureTime,
FNumber,
ExposureProgram,
SpectralSensitivity,
ISOSpeedRatings,
OECF,
Interlace,
TimeZoneOffset,
SelfTimerMode,
SensitivityType,
StandardOutputSensitivity,
RecommendedExposureIndex,
ISOSpeed,
ISOSpeedLatitudeyyy,
ISOSpeedLatitudezzz,
ExifVersion,
DateTimeOriginal,
DateTimeDigitized,
OffsetTime,
OffsetTimeOriginal,
OffsetTimeDigitized,
ComponentsConfiguration,
CompressedBitsPerPixel,
ShutterSpeedValue,
ApertureValue,
BrightnessValue,
ExposureBiasValue,
MaxApertureValue,
SubjectDistance,
MeteringMode,
LightSource,
Flash,
FocalLength,
FlashEnergy2,
SpatialFrequencyResponse2,
Noise,
FocalPlaneXResolution2,
FocalPlaneYResolution2,
FocalPlaneResolutionUnit2,
ImageNumber,
SecurityClassification,
ImageHistory,
SubjectArea,
ExposureIndex2,
TIFFEPStandardID,
SensingMethod2,
MakerNote,
UserComment,
SubsecTime,
SubsecTimeOriginal,
SubsecTimeDigitized,
AmbientTemperature,
Humidity,
Pressure,
WaterDepth,
Acceleration,
CameraElevationAngle,
FlashpixVersion,
ColorSpace,
PixelXDimension,
PixelYDimension,
RelatedSoundFile,
FlashEnergy,
SpatialFrequencyResponse,
FocalPlaneXResolution,
FocalPlaneYResolution,
FocalPlaneResolutionUnit,
SubjectLocation,
ExposureIndex,
SensingMethod,
FileSource,
SceneType,
CFAPattern,
CustomRendered,
ExposureMode,
WhiteBalance,
DigitalZoomRatio,
FocalLengthIn35mmFilm,
SceneCaptureType,
GainControl,
Contrast,
Saturation,
Sharpness,
DeviceSettingDescription,
SubjectDistanceRange,
ImageUniqueID,
OwnerName,
SerialNumber,
LensInfo,
LensMake,
LensModel,
LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
public static readonly ExifTag[] Gps =
{
GPSVersionID,
GPSLatitudeRef,
GPSLatitude,
GPSLongitudeRef,
GPSLongitude,
GPSAltitudeRef,
GPSAltitude,
GPSTimestamp,
GPSSatellites,
GPSStatus,
GPSMeasureMode,
GPSDOP,
GPSSpeedRef,
GPSSpeed,
GPSTrackRef,
GPSTrack,
GPSImgDirectionRef,
GPSImgDirection,
GPSMapDatum,
GPSDestLatitudeRef,
GPSDestLatitude,
GPSDestLongitudeRef,
GPSDestLongitude,
GPSDestBearingRef,
GPSDestBearing,
GPSDestDistanceRef,
GPSDestDistance,
GPSProcessingMethod,
GPSAreaInformation,
GPSDateStamp,
GPSDifferential
};
}
}

191
src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs

@ -13,11 +13,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
public sealed class ExifValue : IEquatable<ExifValue>
{
/// <summary>
/// The exif value.
/// </summary>
private object exifValue;
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class
/// by making a copy from another exif value.
@ -34,30 +29,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (!other.IsArray)
{
this.exifValue = other.exifValue;
this.Value = other.Value;
}
else
{
var array = (Array)other.exifValue;
this.exifValue = array.Clone();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="ExifValue"/> class.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="dataType">The data type.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, bool isArray)
{
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray;
if (dataType == ExifDataType.Ascii)
{
this.IsArray = false;
var array = (Array)other.Value;
this.Value = array.Clone();
}
}
@ -69,51 +46,32 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
: this(tag, dataType, isArray)
{
this.exifValue = value;
this.Tag = tag;
this.DataType = dataType;
this.IsArray = isArray && dataType != ExifDataType.Ascii;
this.Value = value;
}
/// <summary>
/// Gets the data type of the exif value.
/// </summary>
public ExifDataType DataType
{
get;
}
public ExifDataType DataType { get; }
/// <summary>
/// Gets a value indicating whether the value is an array.
/// </summary>
public bool IsArray
{
get;
}
public bool IsArray { get; }
/// <summary>
/// Gets the tag of the exif value.
/// </summary>
public ExifTag Tag
{
get;
}
public ExifTag Tag { get; }
/// <summary>
/// Gets or sets the value.
/// Gets the value.
/// </summary>
public object Value
{
get
{
return this.exifValue;
}
set
{
this.CheckValue(value);
this.exifValue = value;
}
}
public object Value { get; }
/// <summary>
/// Gets a value indicating whether the EXIF value has a value.
@ -122,14 +80,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return false;
}
if (this.DataType == ExifDataType.Ascii)
{
return ((string)this.exifValue).Length > 0;
return ((string)this.Value).Length > 0;
}
return true;
@ -143,7 +101,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
get
{
if (this.exifValue == null)
if (this.Value == null)
{
return 4;
}
@ -163,12 +121,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
if (this.DataType == ExifDataType.Ascii)
{
return Encoding.UTF8.GetBytes((string)this.exifValue).Length;
return Encoding.UTF8.GetBytes((string)this.Value).Length;
}
if (this.IsArray)
{
return ((Array)this.exifValue).Length;
return ((Array)this.Value).Length;
}
return 1;
@ -217,12 +175,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc />
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
return this.Equals(obj as ExifValue);
return obj is ExifValue other && this.Equals(other);
}
/// <inheritdoc />
@ -241,7 +194,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return
this.Tag == other.Tag &&
this.DataType == other.DataType &&
object.Equals(this.exifValue, other.exifValue);
object.Equals(this.Value, other.Value);
}
/// <summary>
/// Clones the current value, overwriting the value.
/// </summary>
/// <param name="value">The value to overwrite.</param>
/// <returns><see cref="ExifValue"/></returns>
public ExifValue WithValue(object value)
{
this.CheckValue(value);
return new ExifValue(this.Tag, this.DataType, value, this.IsArray);
}
/// <inheritdoc/>
@ -253,26 +218,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <inheritdoc/>
public override string ToString()
{
if (this.exifValue == null)
if (this.Value == null)
{
return null;
}
if (this.DataType == ExifDataType.Ascii)
{
return (string)this.exifValue;
return (string)this.Value;
}
if (!this.IsArray)
{
return this.ToString(this.exifValue);
return this.ToString(this.Value);
}
var sb = new StringBuilder();
foreach (object value in (Array)this.exifValue)
foreach (object value in (Array)this.Value)
{
sb.Append(this.ToString(value));
sb.Append(" ");
sb.Append(' ');
}
return sb.ToString();
@ -293,13 +258,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
{
Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
ExifValue exifValue;
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
switch (tag)
{
case ExifTag.ImageDescription:
@ -355,8 +313,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSDestBearingRef:
case ExifTag.GPSDestDistanceRef:
case ExifTag.GPSDateStamp:
exifValue = new ExifValue(tag, ExifDataType.Ascii, true);
break;
return new ExifValue(tag, ExifDataType.Ascii, value, true);
case ExifTag.ClipPath:
case ExifTag.VersionYear:
@ -369,13 +326,12 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.XPKeywords:
case ExifTag.XPSubject:
case ExifTag.GPSVersionID:
exifValue = new ExifValue(tag, ExifDataType.Byte, true);
break;
return new ExifValue(tag, ExifDataType.Byte, value, true);
case ExifTag.FaxProfile:
case ExifTag.ModeNumber:
case ExifTag.GPSAltitudeRef:
exifValue = new ExifValue(tag, ExifDataType.Byte, false);
break;
return new ExifValue(tag, ExifDataType.Byte, value, false);
case ExifTag.FreeOffsets:
case ExifTag.FreeByteCounts:
@ -389,8 +345,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.StripRowCounts:
case ExifTag.IntergraphRegisters:
case ExifTag.TimeZoneOffset:
exifValue = new ExifValue(tag, ExifDataType.Long, true);
break;
return new ExifValue(tag, ExifDataType.Long, value, true);
case ExifTag.SubfileType:
case ExifTag.SubIFDOffset:
case ExifTag.GPSIFDOffset:
@ -412,8 +367,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.FaxRecvParams:
case ExifTag.FaxRecvTime:
case ExifTag.ImageNumber:
exifValue = new ExifValue(tag, ExifDataType.Long, false);
break;
return new ExifValue(tag, ExifDataType.Long, value, false);
case ExifTag.WhitePoint:
case ExifTag.PrimaryChromaticities:
@ -428,8 +382,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSTimestamp:
case ExifTag.GPSDestLatitude:
case ExifTag.GPSDestLongitude:
exifValue = new ExifValue(tag, ExifDataType.Rational, true);
break;
return new ExifValue(tag, ExifDataType.Rational, value, true);
case ExifTag.XPosition:
case ExifTag.YPosition:
case ExifTag.XResolution:
@ -463,8 +417,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.GPSImgDirection:
case ExifTag.GPSDestBearing:
case ExifTag.GPSDestDistance:
exifValue = new ExifValue(tag, ExifDataType.Rational, false);
break;
return new ExifValue(tag, ExifDataType.Rational, value, false);
case ExifTag.BitsPerSample:
case ExifTag.MinSampleValue:
@ -487,8 +440,8 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ISOSpeedRatings:
case ExifTag.SubjectArea:
case ExifTag.SubjectLocation:
exifValue = new ExifValue(tag, ExifDataType.Short, true);
break;
return new ExifValue(tag, ExifDataType.Short, value, true);
case ExifTag.OldSubfileType:
case ExifTag.Compression:
case ExifTag.PhotometricInterpretation:
@ -535,20 +488,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.Sharpness:
case ExifTag.SubjectDistanceRange:
case ExifTag.GPSDifferential:
exifValue = new ExifValue(tag, ExifDataType.Short, false);
break;
return new ExifValue(tag, ExifDataType.Short, value, false);
case ExifTag.Decode:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, true);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, true);
case ExifTag.ShutterSpeedValue:
case ExifTag.BrightnessValue:
case ExifTag.ExposureBiasValue:
case ExifTag.AmbientTemperature:
case ExifTag.WaterDepth:
case ExifTag.CameraElevationAngle:
exifValue = new ExifValue(tag, ExifDataType.SignedRational, false);
break;
return new ExifValue(tag, ExifDataType.SignedRational, value, false);
case ExifTag.JPEGTables:
case ExifTag.OECF:
@ -565,18 +516,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ImageSourceData:
case ExifTag.GPSProcessingMethod:
case ExifTag.GPSAreaInformation:
exifValue = new ExifValue(tag, ExifDataType.Undefined, true);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, true);
case ExifTag.FileSource:
case ExifTag.SceneType:
exifValue = new ExifValue(tag, ExifDataType.Undefined, false);
break;
return new ExifValue(tag, ExifDataType.Undefined, value, false);
case ExifTag.StripOffsets:
case ExifTag.TileByteCounts:
case ExifTag.ImageLayer:
exifValue = CreateNumber(tag, type, true);
break;
return CreateNumber(tag, value, true);
case ExifTag.ImageWidth:
case ExifTag.ImageLength:
case ExifTag.TileWidth:
@ -585,15 +535,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
case ExifTag.ConsecutiveBadFaxLines:
case ExifTag.PixelXDimension:
case ExifTag.PixelYDimension:
exifValue = CreateNumber(tag, type, false);
break;
return CreateNumber(tag, value, false);
default:
throw new NotSupportedException();
}
exifValue.Value = value;
return exifValue;
}
/// <summary>
@ -635,29 +581,35 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// Returns an EXIF value with a numeric type for the given tag.
/// </summary>
/// <param name="tag">The tag.</param>
/// <param name="type">The numeric type.</param>
/// <param name="value">The value.</param>
/// <param name="isArray">Whether the value is an array.</param>
/// <returns>
/// The <see cref="ExifValue"/>.
/// </returns>
private static ExifValue CreateNumber(ExifTag tag, Type type, bool isArray)
private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
{
Type type = value?.GetType();
if (type != null && type.IsArray)
{
type = type.GetElementType();
}
if (type == null || type == typeof(ushort))
{
return new ExifValue(tag, ExifDataType.Short, isArray);
return new ExifValue(tag, ExifDataType.Short, value, isArray);
}
if (type == typeof(short))
{
return new ExifValue(tag, ExifDataType.SignedShort, isArray);
return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
}
if (type == typeof(uint))
{
return new ExifValue(tag, ExifDataType.Long, isArray);
return new ExifValue(tag, ExifDataType.Long, value, isArray);
}
return new ExifValue(tag, ExifDataType.SignedLong, isArray);
return new ExifValue(tag, ExifDataType.SignedLong, value, isArray);
}
/// <summary>
@ -739,8 +691,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// <returns>The <see cref="string"/></returns>
private string ToString(object value)
{
string description = ExifTagDescriptionAttribute.GetDescription(this.Tag, value);
if (description != null)
if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
{
return description;
}
@ -788,7 +739,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
private int GetHashCode(ExifValue exif)
{
int hashCode = exif.Tag.GetHashCode() ^ exif.DataType.GetHashCode();
return hashCode ^ exif.exifValue?.GetHashCode() ?? hashCode;
return hashCode ^ exif.Value?.GetHashCode() ?? hashCode;
}
}
}

410
src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs

@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using SixLabors.ImageSharp.Primitives;
@ -19,299 +19,28 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
/// </summary>
private const int StartIndex = 6;
/// <summary>
/// The collection if Image File Directory tags
/// </summary>
private static readonly ExifTag[] IfdTags =
{
ExifTag.SubfileType,
ExifTag.OldSubfileType,
ExifTag.ImageWidth,
ExifTag.ImageLength,
ExifTag.BitsPerSample,
ExifTag.Compression,
ExifTag.PhotometricInterpretation,
ExifTag.Thresholding,
ExifTag.CellWidth,
ExifTag.CellLength,
ExifTag.FillOrder,
ExifTag.DocumentName,
ExifTag.ImageDescription,
ExifTag.Make,
ExifTag.Model,
ExifTag.StripOffsets,
ExifTag.Orientation,
ExifTag.SamplesPerPixel,
ExifTag.RowsPerStrip,
ExifTag.StripByteCounts,
ExifTag.MinSampleValue,
ExifTag.MaxSampleValue,
ExifTag.XResolution,
ExifTag.YResolution,
ExifTag.PlanarConfiguration,
ExifTag.PageName,
ExifTag.XPosition,
ExifTag.YPosition,
ExifTag.FreeOffsets,
ExifTag.FreeByteCounts,
ExifTag.GrayResponseUnit,
ExifTag.GrayResponseCurve,
ExifTag.T4Options,
ExifTag.T6Options,
ExifTag.ResolutionUnit,
ExifTag.PageNumber,
ExifTag.ColorResponseUnit,
ExifTag.TransferFunction,
ExifTag.Software,
ExifTag.DateTime,
ExifTag.Artist,
ExifTag.HostComputer,
ExifTag.Predictor,
ExifTag.WhitePoint,
ExifTag.PrimaryChromaticities,
ExifTag.ColorMap,
ExifTag.HalftoneHints,
ExifTag.TileWidth,
ExifTag.TileLength,
ExifTag.TileOffsets,
ExifTag.TileByteCounts,
ExifTag.BadFaxLines,
ExifTag.CleanFaxData,
ExifTag.ConsecutiveBadFaxLines,
ExifTag.InkSet,
ExifTag.InkNames,
ExifTag.NumberOfInks,
ExifTag.DotRange,
ExifTag.TargetPrinter,
ExifTag.ExtraSamples,
ExifTag.SampleFormat,
ExifTag.SMinSampleValue,
ExifTag.SMaxSampleValue,
ExifTag.TransferRange,
ExifTag.ClipPath,
ExifTag.XClipPathUnits,
ExifTag.YClipPathUnits,
ExifTag.Indexed,
ExifTag.JPEGTables,
ExifTag.OPIProxy,
ExifTag.ProfileType,
ExifTag.FaxProfile,
ExifTag.CodingMethods,
ExifTag.VersionYear,
ExifTag.ModeNumber,
ExifTag.Decode,
ExifTag.DefaultImageColor,
ExifTag.T82ptions,
ExifTag.JPEGProc,
ExifTag.JPEGInterchangeFormat,
ExifTag.JPEGInterchangeFormatLength,
ExifTag.JPEGRestartInterval,
ExifTag.JPEGLosslessPredictors,
ExifTag.JPEGPointTransforms,
ExifTag.JPEGQTables,
ExifTag.JPEGDCTables,
ExifTag.JPEGACTables,
ExifTag.YCbCrCoefficients,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrSubsampling,
ExifTag.YCbCrPositioning,
ExifTag.ReferenceBlackWhite,
ExifTag.StripRowCounts,
ExifTag.XMP,
ExifTag.Rating,
ExifTag.RatingPercent,
ExifTag.ImageID,
ExifTag.CFARepeatPatternDim,
ExifTag.CFAPattern2,
ExifTag.BatteryLevel,
ExifTag.Copyright,
ExifTag.MDFileTag,
ExifTag.MDScalePixel,
ExifTag.MDLabName,
ExifTag.MDSampleInfo,
ExifTag.MDPrepDate,
ExifTag.MDPrepTime,
ExifTag.MDFileUnits,
ExifTag.PixelScale,
ExifTag.IntergraphPacketData,
ExifTag.IntergraphRegisters,
ExifTag.IntergraphMatrix,
ExifTag.ModelTiePoint,
ExifTag.SEMInfo,
ExifTag.ModelTransform,
ExifTag.ImageLayer,
ExifTag.FaxRecvParams,
ExifTag.FaxSubaddress,
ExifTag.FaxRecvTime,
ExifTag.ImageSourceData,
ExifTag.XPTitle,
ExifTag.XPComment,
ExifTag.XPAuthor,
ExifTag.XPKeywords,
ExifTag.XPSubject,
ExifTag.GDALMetadata,
ExifTag.GDALNoData
};
/// <summary>
/// The collection of Exif tags
/// </summary>
private static readonly ExifTag[] ExifTags =
{
ExifTag.ExposureTime,
ExifTag.FNumber,
ExifTag.ExposureProgram,
ExifTag.SpectralSensitivity,
ExifTag.ISOSpeedRatings,
ExifTag.OECF,
ExifTag.Interlace,
ExifTag.TimeZoneOffset,
ExifTag.SelfTimerMode,
ExifTag.SensitivityType,
ExifTag.StandardOutputSensitivity,
ExifTag.RecommendedExposureIndex,
ExifTag.ISOSpeed,
ExifTag.ISOSpeedLatitudeyyy,
ExifTag.ISOSpeedLatitudezzz,
ExifTag.ExifVersion,
ExifTag.DateTimeOriginal,
ExifTag.DateTimeDigitized,
ExifTag.OffsetTime,
ExifTag.OffsetTimeOriginal,
ExifTag.OffsetTimeDigitized,
ExifTag.ComponentsConfiguration,
ExifTag.CompressedBitsPerPixel,
ExifTag.ShutterSpeedValue,
ExifTag.ApertureValue,
ExifTag.BrightnessValue,
ExifTag.ExposureBiasValue,
ExifTag.MaxApertureValue,
ExifTag.SubjectDistance,
ExifTag.MeteringMode,
ExifTag.LightSource,
ExifTag.Flash,
ExifTag.FocalLength,
ExifTag.FlashEnergy2,
ExifTag.SpatialFrequencyResponse2,
ExifTag.Noise,
ExifTag.FocalPlaneXResolution2,
ExifTag.FocalPlaneYResolution2,
ExifTag.FocalPlaneResolutionUnit2,
ExifTag.ImageNumber,
ExifTag.SecurityClassification,
ExifTag.ImageHistory,
ExifTag.SubjectArea,
ExifTag.ExposureIndex2,
ExifTag.TIFFEPStandardID,
ExifTag.SensingMethod2,
ExifTag.MakerNote,
ExifTag.UserComment,
ExifTag.SubsecTime,
ExifTag.SubsecTimeOriginal,
ExifTag.SubsecTimeDigitized,
ExifTag.AmbientTemperature,
ExifTag.Humidity,
ExifTag.Pressure,
ExifTag.WaterDepth,
ExifTag.Acceleration,
ExifTag.CameraElevationAngle,
ExifTag.FlashpixVersion,
ExifTag.ColorSpace,
ExifTag.PixelXDimension,
ExifTag.PixelYDimension,
ExifTag.RelatedSoundFile,
ExifTag.FlashEnergy,
ExifTag.SpatialFrequencyResponse,
ExifTag.FocalPlaneXResolution,
ExifTag.FocalPlaneYResolution,
ExifTag.FocalPlaneResolutionUnit,
ExifTag.SubjectLocation,
ExifTag.ExposureIndex,
ExifTag.SensingMethod,
ExifTag.FileSource,
ExifTag.SceneType,
ExifTag.CFAPattern,
ExifTag.CustomRendered,
ExifTag.ExposureMode,
ExifTag.WhiteBalance,
ExifTag.DigitalZoomRatio,
ExifTag.FocalLengthIn35mmFilm,
ExifTag.SceneCaptureType,
ExifTag.GainControl,
ExifTag.Contrast,
ExifTag.Saturation,
ExifTag.Sharpness,
ExifTag.DeviceSettingDescription,
ExifTag.SubjectDistanceRange,
ExifTag.ImageUniqueID,
ExifTag.OwnerName,
ExifTag.SerialNumber,
ExifTag.LensInfo,
ExifTag.LensMake,
ExifTag.LensModel,
ExifTag.LensSerialNumber
};
/// <summary>
/// The collection of GPS tags
/// </summary>
private static readonly ExifTag[] GPSTags =
{
ExifTag.GPSVersionID,
ExifTag.GPSLatitudeRef,
ExifTag.GPSLatitude,
ExifTag.GPSLongitudeRef,
ExifTag.GPSLongitude,
ExifTag.GPSAltitudeRef,
ExifTag.GPSAltitude,
ExifTag.GPSTimestamp,
ExifTag.GPSSatellites,
ExifTag.GPSStatus,
ExifTag.GPSMeasureMode,
ExifTag.GPSDOP,
ExifTag.GPSSpeedRef,
ExifTag.GPSSpeed,
ExifTag.GPSTrackRef,
ExifTag.GPSTrack,
ExifTag.GPSImgDirectionRef,
ExifTag.GPSImgDirection,
ExifTag.GPSMapDatum,
ExifTag.GPSDestLatitudeRef,
ExifTag.GPSDestLatitude,
ExifTag.GPSDestLongitudeRef,
ExifTag.GPSDestLongitude,
ExifTag.GPSDestBearingRef,
ExifTag.GPSDestBearing,
ExifTag.GPSDestDistanceRef,
ExifTag.GPSDestDistance,
ExifTag.GPSProcessingMethod,
ExifTag.GPSAreaInformation,
ExifTag.GPSDateStamp,
ExifTag.GPSDifferential
};
/// <summary>
/// Which parts will be written.
/// </summary>
private ExifParts allowedParts;
private Collection<ExifValue> values;
private Collection<int> dataOffsets;
private Collection<int> ifdIndexes;
private Collection<int> exifIndexes;
private Collection<int> gpsIndexes;
private IList<ExifValue> values;
private List<int> dataOffsets;
private List<int> ifdIndexes;
private List<int> exifIndexes;
private List<int> gpsIndexes;
/// <summary>
/// Initializes a new instance of the <see cref="ExifWriter"/> class.
/// </summary>
/// <param name="values">The values.</param>
/// <param name="allowedParts">The allowed parts.</param>
public ExifWriter(Collection<ExifValue> values, ExifParts allowedParts)
public ExifWriter(IList<ExifValue> values, ExifParts allowedParts)
{
this.values = values;
this.allowedParts = allowedParts;
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, IfdTags);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, GPSTags);
this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
}
/// <summary>
@ -360,6 +89,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
length += 10 + 4 + 2;
byte[] result = new byte[length];
result[0] = (byte)'E';
result[1] = (byte)'x';
result[2] = (byte)'i';
@ -377,17 +107,17 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (exifLength > 0)
{
this.values[exifIndex].Value = ifdOffset + ifdLength;
this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
}
if (gpsLength > 0)
{
this.values[gpsIndex].Value = ifdOffset + ifdLength + exifLength;
this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength);
}
i = Write(BitConverter.GetBytes(ifdOffset), result, i);
i = WriteUInt32(ifdOffset, result, i);
i = this.WriteHeaders(this.ifdIndexes, result, i);
i = Write(BitConverter.GetBytes(thumbnailOffset), result, i);
i = WriteUInt32(thumbnailOffset, result, i);
i = this.WriteData(this.ifdIndexes, result, i);
if (exifLength > 0)
@ -402,19 +132,61 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
i = this.WriteData(this.gpsIndexes, result, i);
}
Write(BitConverter.GetBytes((ushort)0), result, i);
WriteUInt16((ushort)0, result, i);
return result;
}
private static int Write(byte[] source, byte[] destination, int offset)
private static unsafe int WriteSingle(float value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), *((int*)&value));
return offset + 4;
}
private static unsafe int WriteDouble(double value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt64LittleEndian(destination.Slice(offset, 8), *((long*)&value));
return offset + 8;
}
private static int Write(ReadOnlySpan<byte> source, Span<byte> destination, int offset)
{
Buffer.BlockCopy(source, 0, destination, offset, source.Length);
source.CopyTo(destination.Slice(offset, source.Length));
return offset + source.Length;
}
private int GetIndex(Collection<int> indexes, ExifTag tag)
private static int WriteInt16(short value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt16(ushort value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt16LittleEndian(destination.Slice(offset, 2), value);
return offset + 2;
}
private static int WriteUInt32(uint value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private static int WriteInt32(int value, Span<byte> destination, int offset)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), value);
return offset + 4;
}
private int GetIndex(IList<int> indexes, ExifTag tag)
{
foreach (int index in indexes)
{
@ -430,14 +202,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newIndex;
}
private Collection<int> GetIndexes(ExifParts part, ExifTag[] tags)
private List<int> GetIndexes(ExifParts part, ExifTag[] tags)
{
if (((int)this.allowedParts & (int)part) == 0)
{
return new Collection<int>();
return new List<int>();
}
Collection<int> result = new Collection<int>();
var result = new List<int>();
for (int i = 0; i < this.values.Count; i++)
{
ExifValue value = this.values[i];
@ -457,7 +229,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return result;
}
private uint GetLength(IEnumerable<int> indexes)
private uint GetLength(IList<int> indexes)
{
uint length = 0;
@ -494,7 +266,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteData(Collection<int> indexes, byte[] destination, int offset)
private int WriteData(List<int> indexes, byte[] destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@ -509,7 +281,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
ExifValue value = this.values[index];
if (value.Length > 4)
{
Write(BitConverter.GetBytes(newOffset - StartIndex), destination, this.dataOffsets[i++]);
WriteUInt32((uint)(newOffset - StartIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset);
}
}
@ -517,11 +289,11 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteHeaders(Collection<int> indexes, byte[] destination, int offset)
private int WriteHeaders(List<int> indexes, byte[] destination, int offset)
{
this.dataOffsets = new Collection<int>();
this.dataOffsets = new List<int>();
int newOffset = Write(BitConverter.GetBytes((ushort)indexes.Count), destination, offset);
int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset);
if (indexes.Count == 0)
{
@ -531,9 +303,9 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
foreach (int index in indexes)
{
ExifValue value = this.values[index];
newOffset = Write(BitConverter.GetBytes((ushort)value.Tag), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((ushort)value.DataType), destination, newOffset);
newOffset = Write(BitConverter.GetBytes((uint)value.NumberOfComponents), destination, newOffset);
newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
if (value.Length > 4)
{
@ -550,23 +322,19 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
private int WriteRational(Rational value, byte[] destination, int offset)
private static void WriteRational(Span<byte> destination, in Rational value)
{
Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
private int WriteSignedRational(SignedRational value, byte[] destination, int offset)
private static void WriteSignedRational(Span<byte> destination, in SignedRational value)
{
Write(BitConverter.GetBytes(value.Numerator), destination, offset);
Write(BitConverter.GetBytes(value.Denominator), destination, offset + 4);
return offset + 8;
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(0, 4), value.Numerator);
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), value.Denominator);
}
private int WriteValue(ExifDataType dataType, object value, byte[] destination, int offset)
private int WriteValue(ExifDataType dataType, object value, Span<byte> destination, int offset)
{
switch (dataType)
{
@ -577,24 +345,26 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
destination[offset] = (byte)value;
return offset + 1;
case ExifDataType.DoubleFloat:
return Write(BitConverter.GetBytes((double)value), destination, offset);
return WriteDouble((double)value, destination, offset);
case ExifDataType.Short:
return Write(BitConverter.GetBytes((ushort)value), destination, offset);
return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long:
return Write(BitConverter.GetBytes((uint)value), destination, offset);
return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational:
return this.WriteRational((Rational)value, destination, offset);
WriteRational(destination.Slice(offset, 8), (Rational)value);
return offset + 8;
case ExifDataType.SignedByte:
destination[offset] = unchecked((byte)((sbyte)value));
return offset + 1;
case ExifDataType.SignedLong:
return Write(BitConverter.GetBytes((int)value), destination, offset);
return WriteInt32((int)value, destination, offset);
case ExifDataType.SignedShort:
return Write(BitConverter.GetBytes((short)value), destination, offset);
return WriteInt16((short)value, destination, offset);
case ExifDataType.SignedRational:
return this.WriteSignedRational((SignedRational)value, destination, offset);
WriteSignedRational(destination.Slice(offset, 8), (SignedRational)value);
return offset + 8;
case ExifDataType.SingleFloat:
return Write(BitConverter.GetBytes((float)value), destination, offset);
return WriteSingle((float)value, destination, offset);
default:
throw new NotImplementedException();
}

2
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccParametricCurve.cs

@ -158,7 +158,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true;
}
return obj is IccParametricCurve && this.Equals((IccParametricCurve)obj);
return obj is IccParametricCurve other && this.Equals(other);
}
/// <inheritdoc/>

2
src/ImageSharp/MetaData/Profiles/ICC/Curves/IccResponseCurve.cs

@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Icc
return true;
}
return obj is IccResponseCurve && this.Equals((IccResponseCurve)obj);
return obj is IccResponseCurve other && this.Equals(other);
}
/// <inheritdoc />

18
src/ImageSharp/PixelFormats/Alpha8.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing a single 8 bit normalized W values.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;0, 0, 0, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [0, 0, 0, 1] in vector form.
/// </para>
/// </summary>
public struct Alpha8 : IPixel<Alpha8>, IPackedVector<byte>
@ -62,6 +62,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Alpha8> CreatePixelOperations() => new PixelOperations<Alpha8>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -124,7 +138,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>True if the object is equal to the packed vector.</returns>
public override bool Equals(object obj)
{
return (obj is Alpha8) && this.Equals((Alpha8)obj);
return obj is Alpha8 other && this.Equals(other);
}
/// <summary>

20
src/ImageSharp/PixelFormats/Argb32.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in alpha, red, green, and blue order.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
/// <remarks>
@ -237,6 +237,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Argb32> CreatePixelOperations() => new PixelOperations<Argb32>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
@ -321,7 +335,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(float x, float y, float z, float w)
{
Vector4 value = new Vector4(x, y, z, w);
var value = new Vector4(x, y, z, w);
return Pack(ref value);
}
@ -347,7 +361,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Pack(ref Vector3 vector)
{
Vector4 value = new Vector4(vector, 1);
var value = new Vector4(vector, 1);
return Pack(ref value);
}

20
src/ImageSharp/PixelFormats/Bgr24.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -11,6 +10,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red order.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgr24 : IPixel<Bgr24>
@ -57,7 +59,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj?.GetType() == typeof(Bgr24) && this.Equals((Bgr24)obj);
return obj is Bgr24 other && this.Equals(other);
}
/// <inheritdoc/>
@ -80,6 +82,20 @@ namespace SixLabors.ImageSharp.PixelFormats
this = source.Bgr;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

16
src/ImageSharp/PixelFormats/Bgr565.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x and z components use 5 bits, and the y component uses 6 bits.
/// <para>
/// Ranges from &lt;0, 0, 0, 1&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Bgr565 : IPixel<Bgr565>, IPackedVector<ushort>
@ -85,6 +85,20 @@ namespace SixLabors.ImageSharp.PixelFormats
(this.PackedValue & 0x1F) * (1F / 31F));
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

19
src/ImageSharp/PixelFormats/Bgra32.cs

@ -11,6 +11,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in blue, green, red, and alpha order.
/// <para>
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Bgra32 : IPixel<Bgra32>, IPackedVector<uint>
@ -101,7 +104,7 @@ namespace SixLabors.ImageSharp.PixelFormats
}
/// <inheritdoc/>
public override bool Equals(object obj) => obj?.GetType() == typeof(Bgra32) && this.Equals((Bgra32)obj);
public override bool Equals(object obj) => obj is Bgra32 other && this.Equals(other);
/// <inheritdoc/>
public override int GetHashCode()
@ -116,6 +119,20 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

18
src/ImageSharp/PixelFormats/Bgra4444.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing unsigned normalized values, ranging from 0 to 1, using 4 bits each for x, y, z, and w.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Bgra4444 : IPixel<Bgra4444>, IPackedVector<ushort>
@ -70,6 +70,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Bgra4444> CreatePixelOperations() => new PixelOperations<Bgra4444>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
@ -142,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra4444) && this.Equals((Bgra4444)obj);
return obj is Bgra4444 other && this.Equals(other);
}
/// <inheritdoc />

28
src/ImageSharp/PixelFormats/Bgra5551.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing unsigned normalized values ranging from 0 to 1. The x , y and z components use 5 bits, and the w component uses 1 bit.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Bgra5551 : IPixel<Bgra5551>, IPackedVector<ushort>
@ -72,6 +72,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Bgra5551> CreatePixelOperations() => new PixelOperations<Bgra5551>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
@ -101,7 +115,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -111,7 +125,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -122,7 +136,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -132,7 +146,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -142,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is Bgra5551) && this.Equals((Bgra5551)obj);
return obj is Bgra5551 other && this.Equals(other);
}
/// <inheritdoc />
@ -190,6 +204,6 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4() => this.ToVector4() * 255f;
private Vector4 ToByteScaledVector4() => this.ToVector4() * 255f;
}
}

28
src/ImageSharp/PixelFormats/Byte4.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 8-bit unsigned integer values, ranging from 0 to 255.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [255, 255, 255, 255] in vector form.
/// </para>
/// </summary>
public struct Byte4 : IPixel<Byte4>, IPackedVector<uint>
@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="w">The w-component</param>
public Byte4(float x, float y, float z, float w)
{
Vector4 vector = new Vector4(x, y, z, w);
var vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
@ -73,6 +73,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Byte4> CreatePixelOperations() => new PixelOperations<Byte4>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector * 255F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4() / 255F;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -95,14 +109,14 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
this.PackFromVector4(source.ToUnscaledVector4());
this.PackFromVector4(source.ToByteScaledVector4());
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -112,7 +126,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -123,7 +137,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -133,7 +147,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;

6
src/ImageSharp/PixelFormats/ColorBuilder{TPixel}.cs

@ -32,8 +32,8 @@ namespace SixLabors.ImageSharp.PixelFormats
throw new ArgumentException("Hexadecimal string is not in the correct format.", nameof(hex));
}
TPixel result = default(TPixel);
Rgba32 rgba = new Rgba32(
TPixel result = default;
var rgba = new Rgba32(
(byte)(packedValue >> 24),
(byte)(packedValue >> 16),
(byte)(packedValue >> 8),
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <returns>Returns a <typeparamref name="TPixel"/> that represents the color defined by the provided RGBA values.</returns>
public static TPixel FromRGBA(byte red, byte green, byte blue, byte alpha)
{
TPixel color = default(TPixel);
TPixel color = default;
color.PackFromRgba32(new Rgba32(red, green, blue, alpha));
return color;
}

112
src/ImageSharp/PixelFormats/Generated/PixelOperations{TPixel}.Generated.cs

@ -10,8 +10,8 @@ namespace SixLabors.ImageSharp.PixelFormats
public partial class PixelOperations<TPixel>
{
/// <summary>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgba32"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgba32"/> data.</param>
@ -19,8 +19,8 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgba32(ReadOnlySpan<Rgba32> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgba32 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
@ -30,11 +30,11 @@ namespace SixLabors.ImageSharp.PixelFormats
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="PackFromRgba32(ReadOnlySpan{Rgba32}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgba32"/> layout.
/// </summary>
@ -44,10 +44,10 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgba32Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgba32(sourceBytes.NonPortableCast<byte, Rgba32>(), destPixels, count);
this.PackFromRgba32(MemoryMarshal.Cast<byte, Rgba32>(sourceBytes), destPixels, count);
}
/// <summary>
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgba32"/>-s.
/// Bulk version of <see cref="IPixel.ToRgba32(ref Rgba32)"/>.
/// </summary>
@ -69,20 +69,20 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="ToRgba32(ReadOnlySpan{TPixel}, Span{Rgba32}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgba32"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgba32Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgba32(sourceColors, destBytes.NonPortableCast<byte, Rgba32>(), count);
this.ToRgba32(sourceColors, MemoryMarshal.Cast<byte, Rgba32>(destBytes), count);
}
/// <summary>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Bgra32"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Bgra32"/> data.</param>
@ -90,22 +90,22 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromBgra32(ReadOnlySpan<Bgra32> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgra32 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
var rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba = Unsafe.Add(ref sourceRef, i).ToRgba32();
dp.PackFromRgba32(rgba);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="PackFromBgra32(ReadOnlySpan{Bgra32}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Bgra32"/> layout.
/// </summary>
@ -115,10 +115,10 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromBgra32Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromBgra32(sourceBytes.NonPortableCast<byte, Bgra32>(), destPixels, count);
this.PackFromBgra32(MemoryMarshal.Cast<byte, Bgra32>(sourceBytes), destPixels, count);
}
/// <summary>
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Bgra32"/>-s.
/// Bulk version of <see cref="IPixel.ToBgra32(ref Bgra32)"/>.
/// </summary>
@ -140,20 +140,20 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="ToBgra32(ReadOnlySpan{TPixel}, Span{Bgra32}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Bgra32"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToBgra32Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToBgra32(sourceColors, destBytes.NonPortableCast<byte, Bgra32>(), count);
this.ToBgra32(sourceColors, MemoryMarshal.Cast<byte, Bgra32>(destBytes), count);
}
/// <summary>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Rgb24"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Rgb24"/> data.</param>
@ -161,22 +161,22 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromRgb24(ReadOnlySpan<Rgb24> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Rgb24 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
Rgba32 rgba = new Rgba32(0, 0, 0, 255);
var rgba = new Rgba32(0, 0, 0, 255);
for (int i = 0; i < count; i++)
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba.Rgb = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="PackFromRgb24(ReadOnlySpan{Rgb24}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Rgb24"/> layout.
/// </summary>
@ -186,10 +186,10 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromRgb24Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromRgb24(sourceBytes.NonPortableCast<byte, Rgb24>(), destPixels, count);
this.PackFromRgb24(MemoryMarshal.Cast<byte, Rgb24>(sourceBytes), destPixels, count);
}
/// <summary>
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Rgb24"/>-s.
/// Bulk version of <see cref="IPixel.ToRgb24(ref Rgb24)"/>.
/// </summary>
@ -211,20 +211,20 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="ToRgb24(ReadOnlySpan{TPixel}, Span{Rgb24}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Rgb24"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToRgb24Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToRgb24(sourceColors, destBytes.NonPortableCast<byte, Rgb24>(), count);
this.ToRgb24(sourceColors, MemoryMarshal.Cast<byte, Rgb24>(destBytes), count);
}
/// <summary>
/// <summary>
/// Converts 'count' elements in 'source` span of <see cref="Bgr24"/> data to a span of <typeparamref name="TPixel"/>-s.
/// </summary>
/// <param name="source">The source <see cref="Span{T}"/> of <see cref="Bgr24"/> data.</param>
@ -232,8 +232,8 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromBgr24(ReadOnlySpan<Bgr24> source, Span<TPixel> destPixels, int count)
{
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
GuardSpans(source, nameof(source), destPixels, nameof(destPixels), count);
ref Bgr24 sourceRef = ref MemoryMarshal.GetReference(source);
ref TPixel destRef = ref MemoryMarshal.GetReference(destPixels);
@ -243,11 +243,11 @@ namespace SixLabors.ImageSharp.PixelFormats
{
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
rgba.Bgr = Unsafe.Add(ref sourceRef, i);
dp.PackFromRgba32(rgba);
dp.PackFromRgba32(rgba);
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="PackFromBgr24(ReadOnlySpan{Bgr24}, Span{TPixel}, int)"/> that expects a byte span.
/// The layout of the data in 'sourceBytes' must be compatible with <see cref="Bgr24"/> layout.
/// </summary>
@ -257,10 +257,10 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void PackFromBgr24Bytes(ReadOnlySpan<byte> sourceBytes, Span<TPixel> destPixels, int count)
{
this.PackFromBgr24(sourceBytes.NonPortableCast<byte, Bgr24>(), destPixels, count);
this.PackFromBgr24(MemoryMarshal.Cast<byte, Bgr24>(sourceBytes), destPixels, count);
}
/// <summary>
/// <summary>
/// Converts 'count' pixels in 'sourcePixels` span to a span of <see cref="Bgr24"/>-s.
/// Bulk version of <see cref="IPixel.ToBgr24(ref Bgr24)"/>.
/// </summary>
@ -282,18 +282,18 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// <summary>
/// A helper for <see cref="ToBgr24(ReadOnlySpan{TPixel}, Span{Bgr24}, int)"/> that expects a byte span as destination.
/// The layout of the data in 'destBytes' must be compatible with <see cref="Bgr24"/> layout.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="Span{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ToBgr24Bytes(ReadOnlySpan<TPixel> sourceColors, Span<byte> destBytes, int count)
{
this.ToBgr24(sourceColors, destBytes.NonPortableCast<byte, Bgr24>(), count);
this.ToBgr24(sourceColors, MemoryMarshal.Cast<byte, Bgr24>(destBytes), count);
}
}
}
}

32
src/ImageSharp/PixelFormats/HalfSingle.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -10,7 +9,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing a single 16 bit floating point value.
/// <para>
/// Ranges from &lt;0, 0, 0, 1&gt; to &lt;1, 0, 0, 1&gt; in vector form.
/// Ranges from [-1, 0, 0, 1] to [1, 0, 0, 1] in vector form.
/// </para>
/// </summary>
public struct HalfSingle : IPixel<HalfSingle>, IPackedVector<ushort>
@ -86,6 +85,25 @@ namespace SixLabors.ImageSharp.PixelFormats
return HalfTypeHelper.Unpack(this.PackedValue);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
float scaled = vector.X;
scaled *= 2F;
scaled -= 1F;
this.PackedValue = HalfTypeHelper.Pack(scaled);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
float single = this.ToSingle() + 1F;
single /= 2F;
return new Vector4(single, 0, 0, 1);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -111,7 +129,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -121,7 +139,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -132,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -142,7 +160,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -176,7 +194,7 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
var vector = this.ToVector4();
vector *= MaxBytes;

4
src/ImageSharp/PixelFormats/HalfTypeHelper.cs

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ushort Pack(float value)
{
Uif uif = new Uif { F = value };
var uif = new Uif { F = value };
return Pack(uif.I);
}
@ -113,7 +113,7 @@ namespace SixLabors.ImageSharp.PixelFormats
result = ((((uint)value & 0x8000) << 16) | ((((((uint)value >> 10) & 0x1f) - 15) + 127) << 23)) | (mantissa << 13);
}
Uif uif = new Uif { U = result };
var uif = new Uif { U = result };
return uif.F;
}

35
src/ImageSharp/PixelFormats/HalfVector2.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing two 16-bit floating-point values.
/// <para>
/// Ranges from &lt;0, 0, 0, 1&gt; to &lt;1, 0, 0, 1&gt; in vector form.
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// </summary>
public struct HalfVector2 : IPixel<HalfVector2>, IPackedVector<uint>
@ -99,6 +99,25 @@ namespace SixLabors.ImageSharp.PixelFormats
return vector;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F;
scaled -= Vector2.One;
this.PackedValue = Pack(scaled.X, scaled.Y);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2F;
return new Vector4(scaled, 0F, 1F);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -110,7 +129,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()
{
Vector2 vector = this.ToVector2();
var vector = this.ToVector2();
return new Vector4(vector.X, vector.Y, 0F, 1F);
}
@ -125,7 +144,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -135,7 +154,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -146,7 +165,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -156,7 +175,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -204,9 +223,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);

37
src/ImageSharp/PixelFormats/HalfVector4.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 16-bit floating-point values.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct HalfVector4 : IPixel<HalfVector4>, IPackedVector<ulong>
@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="w">The w-component.</param>
public HalfVector4(float x, float y, float z, float w)
{
Vector4 vector = new Vector4(x, y, z, w);
var vector = new Vector4(x, y, z, w);
this.PackedValue = Pack(ref vector);
}
@ -89,6 +89,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<HalfVector4> CreatePixelOperations() => new PixelOperations<HalfVector4>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
vector *= 2F;
vector -= Vector4.One;
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2F;
return scaled;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -118,7 +137,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -128,7 +147,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -139,7 +158,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -149,7 +168,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -172,7 +191,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is HalfVector4) && this.Equals((HalfVector4)obj);
return obj is HalfVector4 other && this.Equals(other);
}
/// <inheritdoc />
@ -198,9 +217,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= MaxBytes;
vector += Half;
vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes);

14
src/ImageSharp/PixelFormats/IPixel.cs

@ -32,6 +32,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <param name="vector">The vector to create the packed representation from.</param>
void PackFromVector4(Vector4 vector);
/// <summary>
/// Sets the packed representation from a scaled <see cref="Vector4"/>.
/// </summary>
/// <param name="vector">The vector to create the packed representation from.</param>
void PackFromScaledVector4(Vector4 vector);
/// <summary>
/// Expands the packed representation into a scaled <see cref="Vector4"/>
/// with values clamped between <value>0</value> and <value>1</value>.
/// The vector components are typically expanded in least to greatest significance order.
/// </summary>
/// <returns>The <see cref="Vector4"/>.</returns>
Vector4 ToScaledVector4();
/// <summary>
/// Expands the packed representation into a <see cref="Vector4"/>.
/// The vector components are typically expanded in least to greatest significance order.

5
src/ImageSharp/PixelFormats/NamedColors{TPixel}.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.PixelFormats
{
@ -735,9 +736,9 @@ namespace SixLabors.ImageSharp.PixelFormats
private static TPixel[] GetWebSafePalette()
{
Rgba32[] constants = ColorConstants.WebSafeColors;
TPixel[] safe = new TPixel[constants.Length + 1];
var safe = new TPixel[constants.Length + 1];
Span<byte> constantsBytes = constants.AsSpan().NonPortableCast<Rgba32, byte>();
Span<byte> constantsBytes = MemoryMarshal.Cast<Rgba32, byte>(constants.AsSpan());
PixelOperations<TPixel>.Instance.PackFromRgba32Bytes(constantsBytes, safe, constants.Length);
return safe;
}

37
src/ImageSharp/PixelFormats/NormalizedByte2.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed packed pixel type containing two 8-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from &lt;-1, -1, 0, 1&gt; to &lt;1, 1, 0, 1&gt; in vector form.
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// </summary>
public struct NormalizedByte2 : IPixel<NormalizedByte2>, IPackedVector<ushort>
@ -104,6 +104,25 @@ namespace SixLabors.ImageSharp.PixelFormats
(sbyte)((this.PackedValue >> 8) & 0xFF) / 127F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F;
scaled -= Vector2.One;
this.PackedValue = Pack(scaled.X, scaled.Y);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2F;
return new Vector4(scaled, 0F, 1F);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -122,7 +141,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = source.ToUnscaledVector4();
Vector4 vector = source.ToByteScaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -134,7 +153,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -144,7 +163,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -155,7 +174,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -165,7 +184,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = 0;
@ -175,7 +194,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte2) && this.Equals((NormalizedByte2)obj);
return obj is NormalizedByte2 other && this.Equals(other);
}
/// <inheritdoc />
@ -214,9 +233,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;

37
src/ImageSharp/PixelFormats/NormalizedByte4.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 8-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from &lt;-1, -1, -1, -1&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct NormalizedByte4 : IPixel<NormalizedByte4>, IPackedVector<uint>
@ -93,6 +93,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<NormalizedByte4> CreatePixelOperations() => new PixelOperations<NormalizedByte4>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
vector *= 2F;
vector -= Vector4.One;
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2F;
return scaled;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -115,7 +134,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = source.ToUnscaledVector4();
Vector4 vector = source.ToByteScaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -127,7 +146,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -137,7 +156,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -148,7 +167,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -158,7 +177,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -168,7 +187,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedByte4) && this.Equals((NormalizedByte4)obj);
return obj is NormalizedByte4 other && this.Equals(other);
}
/// <inheritdoc />
@ -211,9 +230,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;

35
src/ImageSharp/PixelFormats/NormalizedShort2.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing two 16-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from &lt;-1, -1, 0, 1&gt; to &lt;1, 1, 0, 1&gt; in vector form.
/// Ranges from [-1, -1, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// </summary>
public struct NormalizedShort2 : IPixel<NormalizedShort2>, IPackedVector<uint>
@ -91,6 +91,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<NormalizedShort2> CreatePixelOperations() => new PixelOperations<NormalizedShort2>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
Vector2 scaled = new Vector2(vector.X, vector.Y) * 2F;
scaled -= Vector2.One;
this.PackedValue = Pack(scaled.X, scaled.Y);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector2();
scaled += Vector2.One;
scaled /= 2F;
return new Vector4(scaled, 0F, 1F);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -109,7 +128,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = source.ToUnscaledVector4();
Vector4 vector = source.ToByteScaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -121,7 +140,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
@ -131,7 +150,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
@ -142,7 +161,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
@ -152,7 +171,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = 0;
@ -221,9 +240,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;

37
src/ImageSharp/PixelFormats/NormalizedShort4.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing four 16-bit signed normalized values, ranging from −1 to 1.
/// <para>
/// Ranges from &lt;-1, -1, -1, -1&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [-1, -1, -1, -1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct NormalizedShort4 : IPixel<NormalizedShort4>, IPackedVector<ulong>
@ -93,6 +93,25 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<NormalizedShort4> CreatePixelOperations() => new PixelOperations<NormalizedShort4>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
vector *= 2F;
vector -= Vector4.One;
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
var scaled = this.ToVector4();
scaled += Vector4.One;
scaled /= 2F;
return scaled;
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -117,7 +136,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromRgba32(Rgba32 source)
{
Vector4 vector = source.ToUnscaledVector4();
Vector4 vector = source.ToByteScaledVector4();
vector -= Round;
vector -= Half;
vector -= Round;
@ -129,7 +148,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
@ -139,7 +158,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
@ -150,7 +169,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
@ -160,7 +179,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)MathF.Round(vector.X);
dest.G = (byte)MathF.Round(vector.Y);
dest.B = (byte)MathF.Round(vector.Z);
@ -170,7 +189,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public override bool Equals(object obj)
{
return (obj is NormalizedShort4) && this.Equals((NormalizedShort4)obj);
return obj is NormalizedShort4 other && this.Equals(other);
}
/// <inheritdoc />
@ -217,9 +236,9 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4()
private Vector4 ToByteScaledVector4()
{
Vector4 vector = this.ToVector4();
var vector = this.ToVector4();
vector *= Half;
vector += Round;
vector += Half;

365
src/ImageSharp/PixelFormats/PackedPixelConverterHelper.cs

@ -1,365 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
namespace SixLabors.ImageSharp.PixelFormats
{
/// <summary>
/// Assists with the conversion of known packed pixel formats from one to another.
/// </summary>
internal static class PackedPixelConverterHelper
{
/// <summary>
/// A non operative function. Simply returns the original vector.
/// </summary>
private static readonly Func<Vector4, Vector4> Noop = vector4 => vector4;
/// <summary>
/// Returns the correct scaling function for the given types The compute scale function.
/// </summary>
/// <typeparam name="TPixel">The source pixel format.</typeparam>
/// <typeparam name="TPixel2">The target pixel format.</typeparam>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
public static Func<Vector4, Vector4> ComputeScaleFunction<TPixel, TPixel2>()
{
Type source = typeof(TPixel);
Type target = typeof(TPixel2);
// Normalized standard
if (IsStandardNormalizedType(source))
{
return FromStandardNormalizedType(target);
}
// Standard
if (IsStandardType(source))
{
return FromStandardType(target);
}
// Normalized offsets. All four components.
if (IsOffsetNormalizedType(source))
{
return FromOffsetNormalizedType(target);
}
// Offset. All four components.
if (IsOffsetType(source))
{
return FromOffsetType(target);
}
// Normalized offsets. First component pair only.
if (IsOffsetTwoComponentNormalizedType(source))
{
return FromOffsetTwoComponentNormalizedType(target);
}
// Offsets. First component pair only.
if (IsOffsetTwoComponentType(source))
{
return FromOffsetTwoComponentType(target);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardNormalizedType(Type target)
{
if (IsStandardType(target))
{
return vector4 => 255F * vector4;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range then offset the center down.
return vector4 => (2F * vector4) - Vector4.One;
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * vector4) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from 0 to 255.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromStandardType(Type target)
{
// Scale down
if (IsStandardNormalizedType(target))
{
return vector4 => vector4 / 255F;
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Expand the range, divide, then offset the center down.
return vector4 => ((2F * (vector4 / 255F)) - Vector4.One);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
return vector4 => (65534F * (vector4 / 255F)) - new Vector4(32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -1 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 2F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 2F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetType(target) || IsOffsetTwoComponentType(target))
{
// Multiply out the range, two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector values representing all four components
/// ranging from -32767 to 32767.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetType(Type target)
{
if (IsStandardNormalizedType(target))
{
// Compress the range then offset the center up.
return vector4 => (vector4 / 65534F) + new Vector4(.5F);
}
if (IsStandardType(target))
{
// Compress the range, multiply, then offset the center up.
return vector4 => ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
}
if (IsOffsetNormalizedType(target) || IsOffsetTwoComponentNormalizedType(target))
{
// Compress the range. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentNormalizedType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
// Compress the range then offset the center up for first pair.
Vector4 v = (vector4 / 2F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0F, 1F);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
// Compress the range, multiply, then offset the center up for first pair.
Vector4 v = ((vector4 / 2F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0F, 255F);
};
}
if (IsOffsetNormalizedType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -1F, 1F);
}
if (IsOffsetTwoComponentType(target))
{
// Multiply. Two component won't read the last two values.
return vector4 => (vector4 * 32767F);
}
if (IsOffsetType(target))
{
return vector4 =>
{
// Multiply the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 * 32767F;
return new Vector4(v.X, v.Y, -32767F, 32767F);
};
}
return Noop;
}
/// <summary>
/// Returns the correct conversion function to convert from types having vector with the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="target">The target type</param>
/// <returns>The <see cref="Func{Vector4,Vector4}"/></returns>
private static Func<Vector4, Vector4> FromOffsetTwoComponentType(Type target)
{
if (IsStandardNormalizedType(target))
{
return vector4 =>
{
Vector4 v = (vector4 / 65534F) + new Vector4(.5F);
return new Vector4(v.X, v.Y, 0, 1);
};
}
if (IsStandardType(target))
{
return vector4 =>
{
Vector4 v = ((vector4 / 65534F) * 255F) + new Vector4(127.5F);
return new Vector4(v.X, v.Y, 0, 255F);
};
}
if (IsOffsetType(target))
{
// Copy the first two components and set second pair to 0 and 1 equivalent.
return vector4 => new Vector4(vector4.X, vector4.Y, -32767F, 32767F);
}
if (IsOffsetNormalizedType(target))
{
return vector4 =>
{
// Divide the first two components and set second pair to 0 and 1 equivalent.
Vector4 v = vector4 / 32767F;
return new Vector4(v.X, v.Y, -1F, 1F);
};
}
if (IsOffsetTwoComponentNormalizedType(target))
{
// Divide. Two component won't read the last two values.
return vector4 => (vector4 / 32767F);
}
return Noop;
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardNormalizedType(Type type)
{
return
type == typeof(Alpha8)
|| type == typeof(Argb32)
|| type == typeof(Bgr24)
|| type == typeof(Bgra32)
|| type == typeof(Bgr565)
|| type == typeof(Bgra4444)
|| type == typeof(Bgra5551)
|| type == typeof(HalfSingle)
|| type == typeof(HalfVector2)
|| type == typeof(HalfVector4)
|| type == typeof(Rg32)
|| type == typeof(Rgb24)
|| type == typeof(Rgba32)
|| type == typeof(Rgba64)
|| type == typeof(Rgba1010102);
}
/// <summary>
/// Identifies the type as having vector component values ranging from 0 to 255.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsStandardType(Type type)
{
return type == typeof(Byte4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -1 to 1.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentNormalizedType(Type type)
{
return type == typeof(NormalizedByte2)
|| type == typeof(NormalizedShort2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -1 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetNormalizedType(Type type)
{
return type == typeof(NormalizedByte4)
|| type == typeof(NormalizedShort4);
}
/// <summary>
/// Identifies the type as having vector values representing the first component pair ranging from -32767 to 32767.
/// and the second component pair ranging from 0 to 1.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetTwoComponentType(Type type)
{
return type == typeof(Short2);
}
/// <summary>
/// Identifies the type as having vector values representing all four components ranging from -32767 to 32767.
/// </summary>
/// <param name="type">The type to test.</param>
/// <returns>The <see cref="bool"/></returns>
private static bool IsOffsetType(Type type)
{
return type == typeof(Short4);
}
}
}

42
src/ImageSharp/PixelFormats/PixelOperations{TPixel}.cs

@ -63,6 +63,48 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromScaledVector4(Vector4)"/>
/// </summary>
/// <param name="sourceVectors">The <see cref="Span{T}"/> to the source vectors.</param>
/// <param name="destinationColors">The <see cref="Span{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromScaledVector4(ReadOnlySpan<Vector4> sourceVectors, Span<TPixel> destinationColors, int count)
{
GuardSpans(sourceVectors, nameof(sourceVectors), destinationColors, nameof(destinationColors), count);
ref Vector4 sourceRef = ref MemoryMarshal.GetReference(sourceVectors);
ref TPixel destRef = ref MemoryMarshal.GetReference(destinationColors);
for (int i = 0; i < count; i++)
{
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TPixel dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromScaledVector4(sp);
}
}
/// <summary>
/// Bulk version of <see cref="IPixel.ToScaledVector4()"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="Span{T}"/> to the source colors.</param>
/// <param name="destinationVectors">The <see cref="Span{T}"/> to the destination vectors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToScaledVector4(ReadOnlySpan<TPixel> sourceColors, Span<Vector4> destinationVectors, int count)
{
GuardSpans(sourceColors, nameof(sourceColors), destinationVectors, nameof(destinationVectors), count);
ref TPixel sourceRef = ref MemoryMarshal.GetReference(sourceColors);
ref Vector4 destRef = ref MemoryMarshal.GetReference(destinationVectors);
for (int i = 0; i < count; i++)
{
ref TPixel sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToScaledVector4();
}
}
/// <summary>
/// Verifies that the given 'source' and 'destination' spans are at least of 'minLength' size.
/// Throwing an <see cref="ArgumentException"/> if the condition is not met.

26
src/ImageSharp/PixelFormats/Rg32.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Packed pixel type containing two 16-bit unsigned normalized values ranging from 0 to 1.
/// <para>
/// Ranges from &lt;0, 0, 0, 1&gt; to &lt;1, 1, 0, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 1] to [1, 1, 0, 1] in vector form.
/// </para>
/// </summary>
public struct Rg32 : IPixel<Rg32>, IPackedVector<uint>
@ -89,6 +89,20 @@ namespace SixLabors.ImageSharp.PixelFormats
((this.PackedValue >> 16) & 0xFFFF) / 65535F);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -114,7 +128,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgb24(ref Rgb24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -124,7 +138,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToRgba32(ref Rgba32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -135,7 +149,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgr24(ref Bgr24 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -145,7 +159,7 @@ namespace SixLabors.ImageSharp.PixelFormats
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ToBgra32(ref Bgra32 dest)
{
Vector4 vector = this.ToScaledVector4();
Vector4 vector = this.ToByteScaledVector4();
dest.R = (byte)vector.X;
dest.G = (byte)vector.Y;
dest.B = (byte)vector.Z;
@ -193,6 +207,6 @@ namespace SixLabors.ImageSharp.PixelFormats
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Vector4 ToScaledVector4() => this.ToVector4() * 255f;
private Vector4 ToByteScaledVector4() => this.ToVector4() * 255F;
}
}

19
src/ImageSharp/PixelFormats/Rgb24.cs

@ -11,6 +11,9 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <summary>
/// Pixel type containing three 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue order.
/// <para>
/// Ranges from [0, 0, 0, 1] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct Rgb24 : IPixel<Rgb24>
@ -57,7 +60,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj?.GetType() == typeof(Rgb24) && this.Equals((Rgb24)obj);
return obj is Rgb24 other && this.Equals(other);
}
/// <inheritdoc/>
@ -80,6 +83,20 @@ namespace SixLabors.ImageSharp.PixelFormats
this = Unsafe.As<Rgba32, Rgb24>(ref source);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)

16
src/ImageSharp/PixelFormats/Rgba1010102.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Packed vector type containing unsigned normalized values ranging from 0 to 1.
/// The x, y and z components use 10 bits, and the w component uses 2 bits.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
public struct Rgba1010102 : IPixel<Rgba1010102>, IPackedVector<uint>
@ -79,6 +79,20 @@ namespace SixLabors.ImageSharp.PixelFormats
/// <inheritdoc />
public PixelOperations<Rgba1010102> CreatePixelOperations() => new PixelOperations<Rgba1010102>();
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToVector4()

17
src/ImageSharp/PixelFormats/Rgba32.PixelOperations.cs

@ -115,6 +115,7 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <inheritdoc />
internal override void PackFromVector4(ReadOnlySpan<Vector4> sourceVectors, Span<Rgba32> destColors, int count)
{
GuardSpans(sourceVectors, nameof(sourceVectors), destColors, nameof(destColors), count);
@ -130,8 +131,8 @@ namespace SixLabors.ImageSharp.PixelFormats
if (alignedCount > 0)
{
ReadOnlySpan<float> flatSrc = sourceVectors.Slice(0, alignedCount).NonPortableCast<Vector4, float>();
Span<byte> flatDest = destColors.NonPortableCast<Rgba32, byte>();
ReadOnlySpan<float> flatSrc = MemoryMarshal.Cast<Vector4, float>(sourceVectors.Slice(0, alignedCount));
Span<byte> flatDest = MemoryMarshal.Cast<Rgba32, byte>(destColors);
SimdUtils.BulkConvertNormalizedFloatToByteClampOverflows(flatSrc, flatDest);
}
@ -144,6 +145,18 @@ namespace SixLabors.ImageSharp.PixelFormats
}
}
/// <inheritdoc />
internal override void ToScaledVector4(ReadOnlySpan<Rgba32> sourceColors, Span<Vector4> destinationVectors, int count)
{
this.ToVector4(sourceColors, destinationVectors, count);
}
/// <inheritdoc />
internal override void PackFromScaledVector4(ReadOnlySpan<Vector4> sourceVectors, Span<Rgba32> destinationColors, int count)
{
this.PackFromVector4(sourceVectors, destinationColors, count);
}
/// <inheritdoc />
internal override void PackFromRgba32(ReadOnlySpan<Rgba32> source, Span<Rgba32> destPixels, int count)
{

18
src/ImageSharp/PixelFormats/Rgba32.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
/// <para>
/// Ranges from &lt;0, 0, 0, 0&gt; to &lt;1, 1, 1, 1&gt; in vector form.
/// Ranges from [0, 0, 0, 0] to [1, 1, 1, 1] in vector form.
/// </para>
/// </summary>
/// <remarks>
@ -318,6 +318,20 @@ namespace SixLabors.ImageSharp.PixelFormats
dest.A = this.A;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromScaledVector4(Vector4 vector)
{
this.PackFromVector4(vector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ToScaledVector4()
{
return this.ToVector4();
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PackFromVector4(Vector4 vector)
@ -383,7 +397,7 @@ namespace SixLabors.ImageSharp.PixelFormats
/// </summary>
/// <returns>A <see cref="Vector4"/> of values in [0, 255] </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Vector4 ToUnscaledVector4()
internal Vector4 ToByteScaledVector4()
{
return new Vector4(this.R, this.G, this.B, this.A);
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save