Browse Source

Merge branch 'master' into master

pull/292/head
Nikita Balabaev 8 years ago
committed by GitHub
parent
commit
75efd72e95
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      appveyor.yml
  2. 24
      build.cmd
  3. 113
      build.ps1
  4. 31
      gitversion.yml
  5. 15
      src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs
  6. 46
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  7. 2
      src/ImageSharp/Formats/Gif/LzwDecoder.cs
  8. 2
      src/ImageSharp/Formats/Jpeg/GolangPort/OrigJpegDecoderCore.cs
  9. 65
      src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs
  10. 4
      src/ImageSharp/Image/PixelArea{TPixel}.cs
  11. 2
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  12. 2
      src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs
  13. 6
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  14. 4
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  15. 5
      src/ImageSharp/Quantizers/Box.cs
  16. 178
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  17. 13
      tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs
  18. 8
      tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs
  19. 42
      tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
  20. 1
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
  21. 58
      tests/ImageSharp.Tests/Issues/Issue412.cs
  22. 12
      tests/ImageSharp.Tests/TestImages.cs
  23. 1
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  24. 2
      tests/Images/External
  25. BIN
      tests/Images/Input/Gif/issues/issue403_baddescriptorwidth.gif
  26. BIN
      tests/Images/Input/Gif/issues/issue405_badappextlength252-2.gif
  27. BIN
      tests/Images/Input/Gif/issues/issue405_badappextlength252.gif
  28. BIN
      tests/Images/Input/Gif/kumin.gif
  29. BIN
      tests/Images/Input/Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg
  30. BIN
      tests/Images/Input/Png/icon.png

10
appveyor.yml

@ -4,14 +4,10 @@ image: Visual Studio 2017
# prevent the double build when a branch has an active PR # prevent the double build when a branch has an active PR
skip_branch_with_pr: true skip_branch_with_pr: true
install:
- choco install gitversion.portable -pre -y
before_build: before_build:
- git submodule -q update --init - git submodule -q update --init
- cmd: dotnet --version - cmd: dotnet --version
- ps: c:\ProgramData\chocolatey\lib\gitversion.portable\tools\gitversion.exe /l console /output buildserver
build_script: build_script:
- cmd: build.cmd - cmd: build.cmd
@ -19,8 +15,8 @@ test_script:
- tests\CodeCoverage\CodeCoverage.cmd - tests\CodeCoverage\CodeCoverage.cmd
after_test: after_test:
- cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%GitVersion_NuGetVersion%.nupkg" - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.%APPVEYOR_BUILD_VERSION%.nupkg"
- cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.Drawing.%GitVersion_NuGetVersion%.nupkg" - cmd: appveyor PushArtifact "artifacts\SixLabors.ImageSharp.Drawing.%APPVEYOR_BUILD_VERSION%.nupkg"
deploy: deploy:
# MyGet Deployment for builds & releases # MyGet Deployment for builds & releases

24
build.cmd

@ -1,29 +1,7 @@
@echo Off @echo Off
SET versionCommand= PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '.\build.ps1'"
if not "%GitVersion_NuGetVersion%" == "" (
SET versionCommand=/p:packageversion=%GitVersion_NuGetVersion%
@echo building with version set to '%GitVersion_NuGetVersion%'
)
dotnet restore %versionCommand%
ECHO Building projects
dotnet build -c Release %versionCommand%
if not "%errorlevel%"=="0" goto failure
if not "%CI%" == "True" (
ECHO NOT on CI server running tests
dotnet test ./tests/ImageSharp.Tests/ImageSharp.Tests.csproj --no-build -c Release
)
if not "%errorlevel%"=="0" goto failure
ECHO Packaging projects
dotnet pack ./src/ImageSharp/ -c Release --output ../../artifacts --no-build %versionCommand%
if not "%errorlevel%"=="0" goto failure
dotnet pack ./src/ImageSharp.Drawing/ -c Release --output ../../artifacts --no-build %versionCommand%
if not "%errorlevel%"=="0" goto failure if not "%errorlevel%"=="0" goto failure
:success :success

113
build.ps1

@ -0,0 +1,113 @@
# lets calulat the correct version here
$fallbackVersion = "1.0.0";
$version = ''
$tagRegex = '^v?(\d+\.\d+\.\d+)(-([a-zA-Z]+)\.?(\d*))?$'
# we are running on the build server
$isVersionTag = $env:APPVEYOR_REPO_TAG_NAME -match $tagRegex
if($isVersionTag){
Write-Debug "Building commit tagged with a compatable version number"
$version = $matches[1]
$postTag = $matches[3]
$count = $matches[4]
Write-Debug "version number: ${version} post tag: ${postTag} count: ${count}"
if("$postTag" -ne ""){
$version = "${version}-${postTag}"
}
if("$count" -ne ""){
# for consistancy with previous releases we pad the counter to only 4 places
$padded = $count.Trim().Trim('0').PadLeft(4,"0");
Write-Debug "count '$count', padded '${padded}'"
$version = "${version}${padded}"
}
}else {
Write-Debug "Untagged"
$lastTag = (git tag --list --sort=-taggerdate) | Out-String
$list = $lastTag.Split("`n")
foreach ($tag in $list) {
Write-Debug "testing ${tag}"
$tag = $tag.Trim();
if($tag -match $tagRegex){
Write-Debug "matched ${tag}"
$version = $matches[1];
break;
}
}
if("$version" -eq ""){
$version = $fallbackVersion
Write-Debug "Failed to discover base version Fallback to '${version}'"
}else{
Write-Debug "Discovered base version from tags '${version}'"
}
$buildNumber = $env:APPVEYOR_BUILD_NUMBER
# build number replacement is padded to 6 places
$buildNumber = "$buildNumber".Trim().Trim('0').PadLeft(6,"0");
if("$env:APPVEYOR_PULL_REQUEST_NUMBER" -ne ""){
Write-Debug "building a PR"
$prNumber = "$env:APPVEYOR_PULL_REQUEST_NUMBER".Trim().Trim('0').PadLeft(5,"0");
# this is a PR
$version = "${version}-PullRequest${prNumber}${buildNumber}";
}else{
Write-Debug "building a branch commit"
# this is a general branch commit
$branch = $env:APPVEYOR_REPO_BRANCH
if("$branch" -eq ""){
$branch = ((git rev-parse --abbrev-ref HEAD) | Out-String).Trim()
if("$branch" -eq ""){
$branch = "unknown"
}
}
$branch = $branch.Replace("/","-").ToLower()
if($branch.ToLower() -eq "master"){
$branch = "dev"
}
$version = "${version}-${branch}${buildNumber}";
}
}
if("$env:APPVEYOR_API_URL" -ne ""){
# update appveyor build number for this build
Invoke-RestMethod -Method "PUT" `
-Uri "${env:APPVEYOR_API_URL}api/build" `
-Body "{version:'${version}'}" `
-ContentType "application/json"
}
Write-Host "Building version '${version}'"
dotnet restore /p:packageversion=$version
Write-Host "Building projects"
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
}
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
Write-Host "Packaging projects"
dotnet pack ./src/ImageSharp/ -c Release --output ../../artifacts --no-build /p:packageversion=$version
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }
dotnet pack ./src/ImageSharp.Drawing/ -c Release --output ../../artifacts --no-build /p:packageversion=$version
if ($LASTEXITCODE ){ Exit $LASTEXITCODE }

31
gitversion.yml

@ -1,31 +0,0 @@
# to create a new package you create a new release/tag
# in github appveyor will build it without the -cixxx tag
# it will then be deployable cleanly to nuget.org
branches:
master:
tag: ci
mode: ContinuousDeployment
increment: Minor
prevent-increment-of-merged-branch-version: false
track-merge-target: true
pull-request:
regex: (pull|pull\-requests|pr)[/-]
mode: ContinuousDelivery
tag: PullRequest
increment: Inherit
prevent-increment-of-merged-branch-version: false
tag-number-pattern: '[/-](?<number>\d+)[-/]'
track-merge-target: false
tracks-release-branches: false
is-release-branch: false
otherbranches:
regex: '.*'
mode: ContinuousDeployment
tag: ci
increment: Patch
prevent-increment-of-merged-branch-version: false
track-merge-target: true
is-release-branch: false
ignore:
sha: []

15
src/ImageSharp.Drawing/Processors/FillRegionProcessor.cs

@ -82,8 +82,15 @@ namespace SixLabors.ImageSharp.Drawing.Processors
int maxIntersections = region.MaxIntersections; int maxIntersections = region.MaxIntersections;
float subpixelCount = 4; float subpixelCount = 4;
// we need to offset the pixel grid to account for when we outline a path.
// basically if the line is [1,2] => [3,2] then when outlining at 1 we end up with a region of [0.5,1.5],[1.5, 1.5],[3.5,2.5],[2.5,2.5]
// and this can cause missed fills when not using antialiasing.so we offset the pixel grid by 0.5 in the x & y direction thus causing the#
// region to alline with the pixel grid.
float offset = 0.5f;
if (this.Options.Antialias) if (this.Options.Antialias)
{ {
offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
subpixelCount = this.Options.AntialiasSubpixelDepth; subpixelCount = this.Options.AntialiasSubpixelDepth;
if (subpixelCount < 4) if (subpixelCount < 4)
{ {
@ -117,7 +124,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
float subpixelFractionPoint = subpixelFraction / subpixelCount; float subpixelFractionPoint = subpixelFraction / subpixelCount;
for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction) for (float subPixel = (float)y; subPixel < y + 1; subPixel += subpixelFraction)
{ {
int pointsFound = region.Scan(subPixel, buffer, 0); int pointsFound = region.Scan(subPixel + offset, buffer, 0);
if (pointsFound == 0) if (pointsFound == 0)
{ {
// nothing on this line skip // nothing on this line skip
@ -131,8 +138,8 @@ namespace SixLabors.ImageSharp.Drawing.Processors
// points will be paired up // points will be paired up
float scanStart = buffer[point] - minX; float scanStart = buffer[point] - minX;
float scanEnd = buffer[point + 1] - minX; float scanEnd = buffer[point + 1] - minX;
int startX = (int)MathF.Floor(scanStart); int startX = (int)MathF.Floor(scanStart + offset);
int endX = (int)MathF.Floor(scanEnd); int endX = (int)MathF.Floor(scanEnd + offset);
if (startX >= 0 && startX < scanline.Length) if (startX >= 0 && startX < scanline.Length)
{ {
@ -169,7 +176,7 @@ namespace SixLabors.ImageSharp.Drawing.Processors
{ {
for (int x = 0; x < scanlineWidth; x++) for (int x = 0; x < scanlineWidth; x++)
{ {
if (scanline[x] > 0.5) if (scanline[x] >= 0.5)
{ {
scanline[x] = 1; scanline[x] = 1;
} }

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

@ -7,6 +7,7 @@ using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// The global color table. /// The global color table.
/// </summary> /// </summary>
private byte[] globalColorTable; private Buffer<byte> globalColorTable;
/// <summary> /// <summary>
/// The global color table length /// The global color table length
@ -123,10 +124,10 @@ namespace SixLabors.ImageSharp.Formats.Gif
if (this.logicalScreenDescriptor.GlobalColorTableFlag) if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{ {
this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; this.globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
this.globalColorTable = ArrayPool<byte>.Shared.Rent(this.globalColorTableLength); this.globalColorTable = Buffer<byte>.CreateClean(this.globalColorTableLength);
// Read the global color table from the stream // Read the global color table from the stream
stream.Read(this.globalColorTable, 0, this.globalColorTableLength); stream.Read(this.globalColorTable.Array, 0, this.globalColorTableLength);
} }
// Loop though the respective gif parts and read the data. // Loop though the respective gif parts and read the data.
@ -154,10 +155,15 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.ReadComments(); this.ReadComments();
break; break;
case GifConstants.ApplicationExtensionLabel: case GifConstants.ApplicationExtensionLabel:
this.Skip(12); // No need to read.
// The application extension length should be 11 but we've got test images that incorrectly
// set this to 252.
int appLength = stream.ReadByte();
this.Skip(appLength); // No need to read.
break; break;
case GifConstants.PlainTextLabel: case GifConstants.PlainTextLabel:
this.Skip(13); // Not supported by any known decoder. int plainLength = stream.ReadByte();
this.Skip(plainLength); // Not supported by any known decoder.
break; break;
} }
} }
@ -175,10 +181,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
finally finally
{ {
if (this.globalColorTable != null) this.globalColorTable?.Dispose();
{
ArrayPool<byte>.Shared.Return(this.globalColorTable);
}
} }
return this.image; return this.image;
@ -309,19 +312,19 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
GifImageDescriptor imageDescriptor = this.ReadImageDescriptor(); GifImageDescriptor imageDescriptor = this.ReadImageDescriptor();
byte[] localColorTable = null; Buffer<byte> localColorTable = null;
byte[] indices = null; Buffer<byte> indices = null;
try try
{ {
// Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table.
if (imageDescriptor.LocalColorTableFlag) if (imageDescriptor.LocalColorTableFlag)
{ {
int length = imageDescriptor.LocalColorTableSize * 3; int length = imageDescriptor.LocalColorTableSize * 3;
localColorTable = ArrayPool<byte>.Shared.Rent(length); localColorTable = Buffer<byte>.CreateClean(length);
this.currentStream.Read(localColorTable, 0, length); this.currentStream.Read(localColorTable.Array, 0, length);
} }
indices = ArrayPool<byte>.Shared.Rent(imageDescriptor.Width * imageDescriptor.Height); indices = Buffer<byte>.CreateClean(imageDescriptor.Width * imageDescriptor.Height);
this.ReadFrameIndices(imageDescriptor, indices); this.ReadFrameIndices(imageDescriptor, indices);
this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor); this.ReadFrameColors(indices, localColorTable ?? this.globalColorTable, imageDescriptor);
@ -331,12 +334,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
} }
finally finally
{ {
if (localColorTable != null) localColorTable?.Dispose();
{ indices?.Dispose();
ArrayPool<byte>.Shared.Return(localColorTable);
}
ArrayPool<byte>.Shared.Return(indices);
} }
} }
@ -346,7 +345,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param> /// <param name="imageDescriptor">The <see cref="GifImageDescriptor"/>.</param>
/// <param name="indices">The pixel array to write to.</param> /// <param name="indices">The pixel array to write to.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadFrameIndices(GifImageDescriptor imageDescriptor, byte[] indices) private void ReadFrameIndices(GifImageDescriptor imageDescriptor, Span<byte> indices)
{ {
int dataSize = this.currentStream.ReadByte(); int dataSize = this.currentStream.ReadByte();
using (var lzwDecoder = new LzwDecoder(this.currentStream)) using (var lzwDecoder = new LzwDecoder(this.currentStream))
@ -361,7 +360,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="indices">The indexed pixels.</param> /// <param name="indices">The indexed pixels.</param>
/// <param name="colorTable">The color table containing the available colors.</param> /// <param name="colorTable">The color table containing the available colors.</param>
/// <param name="descriptor">The <see cref="GifImageDescriptor"/></param> /// <param name="descriptor">The <see cref="GifImageDescriptor"/></param>
private void ReadFrameColors(byte[] indices, byte[] colorTable, GifImageDescriptor descriptor) private void ReadFrameColors(Span<byte> indices, Span<byte> colorTable, GifImageDescriptor descriptor)
{ {
int imageWidth = this.logicalScreenDescriptor.Width; int imageWidth = this.logicalScreenDescriptor.Width;
int imageHeight = this.logicalScreenDescriptor.Height; int imageHeight = this.logicalScreenDescriptor.Height;
@ -444,7 +443,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
var rgba = new Rgba32(0, 0, 0, 255); var rgba = new Rgba32(0, 0, 0, 255);
for (int x = descriptor.Left; x < descriptor.Left + descriptor.Width; x++) // #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++)
{ {
int index = indices[i]; int index = indices[i];

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

@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="height">The height of the pixel index array.</param> /// <param name="height">The height of the pixel index array.</param>
/// <param name="dataSize">Size of the data.</param> /// <param name="dataSize">Size of the data.</param>
/// <param name="pixels">The pixel array to decode to.</param> /// <param name="pixels">The pixel array to decode to.</param>
public void DecodePixels(int width, int height, int dataSize, byte[] pixels) public void DecodePixels(int width, int height, int dataSize, Span<byte> pixels)
{ {
Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize)); Guard.MustBeLessThan(dataSize, int.MaxValue, nameof(dataSize));

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

@ -704,7 +704,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort
} }
int th = this.Temp[0] & 0x0f; int th = this.Temp[0] & 0x0f;
if (th > OrigHuffmanTree.MaxTh || (!this.IsProgressive && (th > 1))) if (th > OrigHuffmanTree.MaxTh)
{ {
throw new ImageFormatException("Bad Th value"); throw new ImageFormatException("Bad Th value");
} }

65
src/ImageSharp/Formats/Png/Zlib/ZlibInflateStream.cs

@ -2,10 +2,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Png.Zlib namespace SixLabors.ImageSharp.Formats.Png.Zlib
{ {
@ -14,6 +12,13 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
/// </summary> /// </summary>
internal sealed class ZlibInflateStream : Stream internal sealed class ZlibInflateStream : Stream
{ {
/// <summary>
/// Used to read the Adler-32 and Crc-32 checksums
/// We don't actually use this for anything so it doesn't
/// have to be threadsafe.
/// </summary>
private static readonly byte[] ChecksumBuffer = new byte[4];
/// <summary> /// <summary>
/// The inner raw memory stream /// The inner raw memory stream
/// </summary> /// </summary>
@ -38,9 +43,9 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// The read crc data. /// Whether the crc value has been read.
/// </summary> /// </summary>
private byte[] crcread; private bool crcRead;
/// <summary> /// <summary>
/// The current data remaining to be read /// The current data remaining to be read
@ -149,14 +154,12 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
this.compressedStream.Dispose(); this.compressedStream.Dispose();
this.compressedStream = null; this.compressedStream = null;
if (this.crcread == null) if (!this.crcRead)
{ {
// Consume the trailing 4 bytes // Consume the trailing 4 bytes
this.crcread = new byte[4]; this.innerStream.Read(ChecksumBuffer, 0, 4);
for (int i = 0; i < 4; i++) this.currentDataRemaining -= 4;
{ this.crcRead = true;
this.crcread[i] = (byte)this.innerStream.ReadByte();
}
} }
} }
} }
@ -171,11 +174,6 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
private void InitializeInflateStream() private void InitializeInflateStream()
{ {
// The DICT dictionary identifier identifying the used dictionary.
// The preset dictionary.
bool fdict;
// Read the zlib header : http://tools.ietf.org/html/rfc1950 // Read the zlib header : http://tools.ietf.org/html/rfc1950
// CMF(Compression Method and flags) // CMF(Compression Method and flags)
// This byte is divided into a 4 - bit compression method and a // This byte is divided into a 4 - bit compression method and a
@ -195,30 +193,35 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib
return; return;
} }
if ((cmf & 0x0f) != 8) if ((cmf & 0x0F) == 8)
{ {
throw new Exception($"Bad compression method for ZLIB header: cmf={cmf}"); // CINFO is the base-2 logarithm of the LZ77 window size, minus eight.
} int cinfo = (cmf & 0xF0) >> 4;
// CINFO is the base-2 logarithm of the LZ77 window size, minus eight. if (cinfo > 7)
// int cinfo = ((cmf & (0xf0)) >> 8); {
fdict = (flag & 32) != 0; // Values of CINFO above 7 are not allowed in RFC1950.
// CINFO is not defined in this specification for CM not equal to 8.
throw new ImageFormatException($"Invalid window size for ZLIB header: cinfo={cinfo}");
}
}
else
{
throw new ImageFormatException($"Bad method for ZLIB header: cmf={cmf}");
}
// The preset dictionary.
bool fdict = (flag & 32) != 0;
if (fdict) if (fdict)
{ {
// The DICT dictionary identifier identifying the used dictionary. // We don't need this for inflate so simply skip by the next four bytes.
byte[] dictId = new byte[4]; // https://tools.ietf.org/html/rfc1950#page-6
this.innerStream.Read(ChecksumBuffer, 0, 4);
for (int i = 0; i < 4; i++) this.currentDataRemaining -= 4;
{
// We consume but don't use this.
dictId[i] = (byte)this.innerStream.ReadByte();
this.currentDataRemaining--;
}
} }
// Initialize the deflate Stream. // Initialize the deflate Stream.
this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true); this.compressedStream = new DeflateStream(this, CompressionMode.Decompress, true);
} }
} }
} }

4
src/ImageSharp/Image/PixelArea{TPixel}.cs

@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// The underlying buffer containing the raw pixel data. /// The underlying buffer containing the raw pixel data.
/// </summary> /// </summary>
private Buffer<byte> byteBuffer; private readonly Buffer<byte> byteBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TPixel}"/> class.
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp
this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.RowStride = (width * GetComponentCount(componentOrder)) + padding;
this.Length = this.RowStride * height; this.Length = this.RowStride * height;
this.byteBuffer = new Buffer<byte>(this.Length); this.byteBuffer = Buffer<byte>.CreateClean(this.Length);
} }
/// <summary> /// <summary>

2
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp.Memory
/// </summary> /// </summary>
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="IBuffer2D{T}"/></param> /// <param name="buffer">The <see cref="IBuffer2D{T}"/></param>
/// <param name="rectangle">The rectangel subarea</param> /// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns> /// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this IBuffer2D<T> buffer, Rectangle rectangle) public static BufferArea<T> GetArea<T>(this IBuffer2D<T> buffer, Rectangle rectangle)
where T : struct => new BufferArea<T>(buffer, rectangle); where T : struct => new BufferArea<T>(buffer, rectangle);

2
src/ImageSharp/MetaData/Profiles/Exif/ExifTag.cs

@ -975,7 +975,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
[ExifTagDescription((ushort)71, "Fired, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)71, "Fired, Red-eye reduction, Return detected")]
[ExifTagDescription((ushort)73, "On, Red-eye reduction")] [ExifTagDescription((ushort)73, "On, Red-eye reduction")]
[ExifTagDescription((ushort)77, "On, Red-eye reduction, Return not detected")] [ExifTagDescription((ushort)77, "On, Red-eye reduction, Return not detected")]
[ExifTagDescription((ushort)69, "On, Red-eye reduction, Return detected")] [ExifTagDescription((ushort)79, "On, Red-eye reduction, Return detected")]
[ExifTagDescription((ushort)80, "Off, Red-eye reduction")] [ExifTagDescription((ushort)80, "Off, Red-eye reduction")]
[ExifTagDescription((ushort)88, "Auto, Did not fire, Red-eye reduction")] [ExifTagDescription((ushort)88, "Auto, Did not fire, Red-eye reduction")]
[ExifTagDescription((ushort)89, "Auto, Fired, Red-eye reduction")] [ExifTagDescription((ushort)89, "Auto, Fired, Red-eye reduction")]

6
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal abstract partial class ResamplingWeightedProcessor<TPixel> internal abstract partial class ResamplingWeightedProcessor<TPixel>
{ {
/// <summary> /// <summary>
/// Points to a collection of of weights allocated in <see cref="WeightsBuffer"/>. /// Points to a collection of weights allocated in <see cref="WeightsBuffer"/>.
/// </summary> /// </summary>
internal struct WeightsWindow internal struct WeightsWindow
{ {
@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
/// <summary> /// <summary>
/// Holds the <see cref="WeightsWindow"/> values in an optimized contigous memory region. /// Holds the <see cref="WeightsWindow"/> values in an optimized contiguous memory region.
/// </summary> /// </summary>
internal class WeightsBuffer : IDisposable internal class WeightsBuffer : IDisposable
{ {
@ -196,4 +196,4 @@ namespace SixLabors.ImageSharp.Processing.Processors
} }
} }
} }
} }

4
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -58,9 +58,9 @@ namespace SixLabors.ImageSharp.Processing.Processors
// We will always be creating the clone even for mutate because thats the way this base processor works // We will always be creating the clone even for mutate because thats the way this base processor works
// ------------ // ------------
// For resize we know we are going to populate every pixel with fresh data and we want a different target size so // For resize we know we are going to populate every pixel with fresh data and we want a different target size so
// let's manually clone an empty set of images at the correct target and then have the base class processs them in turn. // let's manually clone an empty set of images at the correct target and then have the base class process them in turn.
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders
var image = new Image<TPixel>(config, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added var image = new Image<TPixel>(config, source.MetaData.Clone(), frames); // base the place holder images in to prevent a extra frame being added
return image; return image;
} }

5
src/ImageSharp/Quantizers/Box.cs

@ -5,9 +5,8 @@ namespace SixLabors.ImageSharp.Quantizers
{ {
/// <summary> /// <summary>
/// Represents a box color cube. /// Represents a box color cube.
/// TODO: This should be a struct for performance
/// </summary> /// </summary>
internal sealed class Box internal struct Box
{ {
/// <summary> /// <summary>
/// Gets or sets the min red value, exclusive. /// Gets or sets the min red value, exclusive.
@ -54,4 +53,4 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary> /// </summary>
public int Volume { get; set; } public int Volume { get; set; }
} }
} }

178
src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

@ -168,20 +168,19 @@ namespace SixLabors.ImageSharp.Quantizers
this.palette = new TPixel[this.colors]; this.palette = new TPixel[this.colors];
for (int k = 0; k < this.colors; k++) for (int k = 0; k < this.colors; k++)
{ {
this.Mark(this.colorCube[k], (byte)k); this.Mark(ref this.colorCube[k], (byte)k);
float weight = Volume(this.colorCube[k], this.vwt); float weight = Volume(ref this.colorCube[k], this.vwt);
if (MathF.Abs(weight) > Constants.Epsilon) if (MathF.Abs(weight) > Constants.Epsilon)
{ {
float r = Volume(this.colorCube[k], this.vmr) / weight; float r = Volume(ref this.colorCube[k], this.vmr);
float g = Volume(this.colorCube[k], this.vmg) / weight; float g = Volume(ref this.colorCube[k], this.vmg);
float b = Volume(this.colorCube[k], this.vmb) / weight; float b = Volume(ref this.colorCube[k], this.vmb);
float a = Volume(this.colorCube[k], this.vma) / weight; float a = Volume(ref this.colorCube[k], this.vma);
var color = default(TPixel); ref TPixel color = ref this.palette[k];
color.PackFromVector4(new Vector4(r, g, b, a) / 255F); color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F);
this.palette[k] = color;
} }
} }
} }
@ -197,19 +196,21 @@ namespace SixLabors.ImageSharp.Quantizers
var rgba = default(Rgba32); var rgba = default(Rgba32);
pixel.ToRgba32(ref rgba); pixel.ToRgba32(ref rgba);
int r = rgba.R >> 2; // 8 - IndexBits int r = rgba.R >> (8 - IndexBits);
int g = rgba.G >> 2; int g = rgba.G >> (8 - IndexBits);
int b = rgba.B >> 2; int b = rgba.B >> (8 - IndexBits);
int a = rgba.A >> 5; // 8 - IndexAlphaBits int a = rgba.A >> (8 - IndexAlphaBits);
int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1);
int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); this.vwt[index]++;
this.vmr[index] += rgba.R;
this.vmg[index] += rgba.G;
this.vmb[index] += rgba.B;
this.vma[index] += rgba.A;
this.vwt[ind]++; var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A);
this.vmr[ind] += r; this.m2[index] += Vector4.Dot(vector, vector);
this.vmg[ind] += g;
this.vmb[ind] += b;
this.vma[ind] += a;
this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -301,7 +302,7 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <param name="moment">The moment.</param> /// <param name="moment">The moment.</param>
/// <returns>The result.</returns> /// <returns>The result.</returns>
private static float Volume(Box cube, long[] moment) private static float Volume(ref Box cube, long[] moment)
{ {
return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
@ -328,12 +329,12 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="direction">The direction.</param> /// <param name="direction">The direction.</param>
/// <param name="moment">The moment.</param> /// <param name="moment">The moment.</param>
/// <returns>The result.</returns> /// <returns>The result.</returns>
private static long Bottom(Box cube, int direction, long[] moment) private static long Bottom(ref Box cube, int direction, long[] moment)
{ {
switch (direction) switch (direction)
{ {
// Red // Red
case 0: case 3:
return -moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)] return -moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
+ moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
@ -344,7 +345,7 @@ namespace SixLabors.ImageSharp.Quantizers
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
// Green // Green
case 1: case 2:
return -moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)] return -moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
@ -355,7 +356,7 @@ namespace SixLabors.ImageSharp.Quantizers
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
// Blue // Blue
case 2: case 1:
return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)] return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
+ moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
@ -366,7 +367,7 @@ namespace SixLabors.ImageSharp.Quantizers
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
// Alpha // Alpha
case 3: case 0:
return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] return -moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
+ moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
+ moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
@ -389,12 +390,12 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="position">The position.</param> /// <param name="position">The position.</param>
/// <param name="moment">The moment.</param> /// <param name="moment">The moment.</param>
/// <returns>The result.</returns> /// <returns>The result.</returns>
private static long Top(Box cube, int direction, int position, long[] moment) private static long Top(ref Box cube, int direction, int position, long[] moment)
{ {
switch (direction) switch (direction)
{ {
// Red // Red
case 0: case 3:
return moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A1)] return moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A1)]
- moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A0)] - moment[GetPaletteIndex(position, cube.G1, cube.B1, cube.A0)]
- moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A1)] - moment[GetPaletteIndex(position, cube.G1, cube.B0, cube.A1)]
@ -405,7 +406,7 @@ namespace SixLabors.ImageSharp.Quantizers
- moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)]; - moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)];
// Green // Green
case 1: case 2:
return moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A1)] return moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A1)]
- moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A0)] - moment[GetPaletteIndex(cube.R1, position, cube.B1, cube.A0)]
- moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A1)] - moment[GetPaletteIndex(cube.R1, position, cube.B0, cube.A1)]
@ -416,7 +417,7 @@ namespace SixLabors.ImageSharp.Quantizers
- moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)]; - moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)];
// Blue // Blue
case 2: case 1:
return moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A1)] return moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A1)]
- moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A0)] - moment[GetPaletteIndex(cube.R1, cube.G1, position, cube.A0)]
- moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A1)] - moment[GetPaletteIndex(cube.R1, cube.G0, position, cube.A1)]
@ -427,7 +428,7 @@ namespace SixLabors.ImageSharp.Quantizers
- moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)]; - moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)];
// Alpha // Alpha
case 3: case 0:
return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, position)] return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, position)]
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, position)] - moment[GetPaletteIndex(cube.R1, cube.G1, cube.B0, position)]
- moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, position)] - moment[GetPaletteIndex(cube.R1, cube.G0, cube.B1, position)]
@ -553,12 +554,12 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary> /// </summary>
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <returns>The <see cref="float"/>.</returns> /// <returns>The <see cref="float"/>.</returns>
private float Variance(Box cube) private float Variance(ref Box cube)
{ {
float dr = Volume(cube, this.vmr); float dr = Volume(ref cube, this.vmr);
float dg = Volume(cube, this.vmg); float dg = Volume(ref cube, this.vmg);
float db = Volume(cube, this.vmb); float db = Volume(ref cube, this.vmb);
float da = Volume(cube, this.vma); float da = Volume(ref cube, this.vma);
float xx = float xx =
this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
@ -578,7 +579,8 @@ namespace SixLabors.ImageSharp.Quantizers
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] - this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
return xx - (((dr * dr) + (dg * dg) + (db * db) + (da * da)) / Volume(cube, this.vwt)); var vector = new Vector4(dr, dg, db, da);
return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt));
} }
/// <summary> /// <summary>
@ -599,38 +601,33 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="wholeA">The whole alpha.</param> /// <param name="wholeA">The whole alpha.</param>
/// <param name="wholeW">The whole weight.</param> /// <param name="wholeW">The whole weight.</param>
/// <returns>The <see cref="float"/>.</returns> /// <returns>The <see cref="float"/>.</returns>
private float Maximize(Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) private float Maximize(ref Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW)
{ {
long baseR = Bottom(cube, direction, this.vmr); long baseR = Bottom(ref cube, direction, this.vmr);
long baseG = Bottom(cube, direction, this.vmg); long baseG = Bottom(ref cube, direction, this.vmg);
long baseB = Bottom(cube, direction, this.vmb); long baseB = Bottom(ref cube, direction, this.vmb);
long baseA = Bottom(cube, direction, this.vma); long baseA = Bottom(ref cube, direction, this.vma);
long baseW = Bottom(cube, direction, this.vwt); long baseW = Bottom(ref cube, direction, this.vwt);
float max = 0F; float max = 0F;
cut = -1; cut = -1;
for (int i = first; i < last; i++) for (int i = first; i < last; i++)
{ {
float halfR = baseR + Top(cube, direction, i, this.vmr); float halfR = baseR + Top(ref cube, direction, i, this.vmr);
float halfG = baseG + Top(cube, direction, i, this.vmg); float halfG = baseG + Top(ref cube, direction, i, this.vmg);
float halfB = baseB + Top(cube, direction, i, this.vmb); float halfB = baseB + Top(ref cube, direction, i, this.vmb);
float halfA = baseA + Top(cube, direction, i, this.vma); float halfA = baseA + Top(ref cube, direction, i, this.vma);
float halfW = baseW + Top(cube, direction, i, this.vwt); float halfW = baseW + Top(ref cube, direction, i, this.vwt);
float temp;
if (MathF.Abs(halfW) < Constants.Epsilon) if (MathF.Abs(halfW) < Constants.Epsilon)
{ {
continue; continue;
} }
temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; var vector = new Vector4(halfR, halfG, halfB, halfA);
float temp = Vector4.Dot(vector, vector) / halfW;
halfR = wholeR - halfR;
halfG = wholeG - halfG;
halfB = wholeB - halfB;
halfA = wholeA - halfA;
halfW = wholeW - halfW; halfW = wholeW - halfW;
if (MathF.Abs(halfW) < Constants.Epsilon) if (MathF.Abs(halfW) < Constants.Epsilon)
@ -638,7 +635,14 @@ namespace SixLabors.ImageSharp.Quantizers
continue; continue;
} }
temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; halfR = wholeR - halfR;
halfG = wholeG - halfG;
halfB = wholeB - halfB;
halfA = wholeA - halfA;
vector = new Vector4(halfR, halfG, halfB, halfA);
temp += Vector4.Dot(vector, vector) / halfW;
if (temp > max) if (temp > max)
{ {
@ -656,24 +660,24 @@ namespace SixLabors.ImageSharp.Quantizers
/// <param name="set1">The first set.</param> /// <param name="set1">The first set.</param>
/// <param name="set2">The second set.</param> /// <param name="set2">The second set.</param>
/// <returns>Returns a value indicating whether the box has been split.</returns> /// <returns>Returns a value indicating whether the box has been split.</returns>
private bool Cut(Box set1, Box set2) private bool Cut(ref Box set1, ref Box set2)
{ {
float wholeR = Volume(set1, this.vmr); float wholeR = Volume(ref set1, this.vmr);
float wholeG = Volume(set1, this.vmg); float wholeG = Volume(ref set1, this.vmg);
float wholeB = Volume(set1, this.vmb); float wholeB = Volume(ref set1, this.vmb);
float wholeA = Volume(set1, this.vma); float wholeA = Volume(ref set1, this.vma);
float wholeW = Volume(set1, this.vwt); float wholeW = Volume(ref set1, this.vwt);
float maxr = this.Maximize(set1, 0, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxg = this.Maximize(set1, 1, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxb = this.Maximize(set1, 2, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); float maxb = this.Maximize(ref set1, 1, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW);
float maxa = this.Maximize(set1, 3, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); float maxa = this.Maximize(ref set1, 0, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW);
int dir; int dir;
if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa))
{ {
dir = 0; dir = 3;
if (cutr < 0) if (cutr < 0)
{ {
@ -682,15 +686,15 @@ namespace SixLabors.ImageSharp.Quantizers
} }
else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa))
{ {
dir = 1; dir = 2;
} }
else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa))
{ {
dir = 2; dir = 1;
} }
else else
{ {
dir = 3; dir = 0;
} }
set2.R1 = set1.R1; set2.R1 = set1.R1;
@ -701,7 +705,7 @@ namespace SixLabors.ImageSharp.Quantizers
switch (dir) switch (dir)
{ {
// Red // Red
case 0: case 3:
set2.R0 = set1.R1 = cutr; set2.R0 = set1.R1 = cutr;
set2.G0 = set1.G0; set2.G0 = set1.G0;
set2.B0 = set1.B0; set2.B0 = set1.B0;
@ -709,7 +713,7 @@ namespace SixLabors.ImageSharp.Quantizers
break; break;
// Green // Green
case 1: case 2:
set2.G0 = set1.G1 = cutg; set2.G0 = set1.G1 = cutg;
set2.R0 = set1.R0; set2.R0 = set1.R0;
set2.B0 = set1.B0; set2.B0 = set1.B0;
@ -717,7 +721,7 @@ namespace SixLabors.ImageSharp.Quantizers
break; break;
// Blue // Blue
case 2: case 1:
set2.B0 = set1.B1 = cutb; set2.B0 = set1.B1 = cutb;
set2.R0 = set1.R0; set2.R0 = set1.R0;
set2.G0 = set1.G0; set2.G0 = set1.G0;
@ -725,7 +729,7 @@ namespace SixLabors.ImageSharp.Quantizers
break; break;
// Alpha // Alpha
case 3: case 0:
set2.A0 = set1.A1 = cuta; set2.A0 = set1.A1 = cuta;
set2.R0 = set1.R0; set2.R0 = set1.R0;
set2.G0 = set1.G0; set2.G0 = set1.G0;
@ -744,7 +748,7 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary> /// </summary>
/// <param name="cube">The cube.</param> /// <param name="cube">The cube.</param>
/// <param name="label">A label.</param> /// <param name="label">A label.</param>
private void Mark(Box cube, byte label) private void Mark(ref Box cube, byte label)
{ {
for (int r = cube.R0 + 1; r <= cube.R1; r++) for (int r = cube.R0 + 1; r <= cube.R1; r++)
{ {
@ -769,23 +773,21 @@ namespace SixLabors.ImageSharp.Quantizers
this.colorCube = new Box[this.colors]; this.colorCube = new Box[this.colors];
float[] vv = new float[this.colors]; float[] vv = new float[this.colors];
for (int i = 0; i < this.colors; i++) ref var cube = ref this.colorCube[0];
{ cube.R0 = cube.G0 = cube.B0 = cube.A0 = 0;
this.colorCube[i] = new Box(); cube.R1 = cube.G1 = cube.B1 = IndexCount - 1;
} cube.A1 = IndexAlphaCount - 1;
this.colorCube[0].R0 = this.colorCube[0].G0 = this.colorCube[0].B0 = this.colorCube[0].A0 = 0;
this.colorCube[0].R1 = this.colorCube[0].G1 = this.colorCube[0].B1 = IndexCount - 1;
this.colorCube[0].A1 = IndexAlphaCount - 1;
int next = 0; int next = 0;
for (int i = 1; i < this.colors; i++) for (int i = 1; i < this.colors; i++)
{ {
if (this.Cut(this.colorCube[next], this.colorCube[i])) ref var nextCube = ref this.colorCube[next];
ref var currentCube = ref this.colorCube[i];
if (this.Cut(ref nextCube, ref currentCube))
{ {
vv[next] = this.colorCube[next].Volume > 1 ? this.Variance(this.colorCube[next]) : 0F; vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0F;
vv[i] = this.colorCube[i].Volume > 1 ? this.Variance(this.colorCube[i]) : 0F; vv[i] = currentCube.Volume > 1 ? this.Variance(ref currentCube) : 0F;
} }
else else
{ {
@ -805,7 +807,7 @@ namespace SixLabors.ImageSharp.Quantizers
} }
} }
if (temp <= 0.0) if (temp <= 0F)
{ {
this.colors = i + 1; this.colors = i + 1;
break; break;

13
tests/ImageSharp.Benchmarks/Color/RgbToYCbCr.cs

@ -183,17 +183,6 @@ namespace SixLabors.ImageSharp.Benchmarks
Vector3 vectorCb = VectorCb * vectorRgb; Vector3 vectorCb = VectorCb * vectorRgb;
Vector3 vectorCr = VectorCr * vectorRgb; Vector3 vectorCr = VectorCr * vectorRgb;
// Should be better in theory, but came out to be worse: :(
// Vector3 c = new Vector3(0, 128, 128);
// Vector3 xx = new Vector3(vectorY.X, vectorCb.X, vectorCr.X);
// Vector3 yy = new Vector3(vectorY.Y, -vectorCb.Y, -vectorCr.Y);
// Vector3 zz = new Vector3(vectorY.Z, vectorCb.Z, -vectorCr.Z);
// c += xx + yy + zz;
// *yPtr++ = c.X;
// *cbPtr++ = c.Y;
// *crPtr++ = c.Z;
*yPtr++ = vectorY.X + vectorY.Y + vectorY.Z; *yPtr++ = vectorY.X + vectorY.Y + vectorY.Z;
*cbPtr++ = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z); *cbPtr++ = 128 + (vectorCb.X - vectorCb.Y + vectorCb.Z);
*crPtr++ = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z); *crPtr++ = 128 + (vectorCr.X - vectorCr.Y - vectorCr.Z);
@ -375,4 +364,4 @@ namespace SixLabors.ImageSharp.Benchmarks
} }
} }
} }
} }

8
tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs

@ -81,13 +81,13 @@ namespace SixLabors.ImageSharp.Tests.Drawing
using (PixelAccessor<Rgba32> sourcePixels = image.Lock()) using (PixelAccessor<Rgba32> sourcePixels = image.Lock())
{ {
Assert.Equal(Rgba32.HotPink, sourcePixels[11, 11]); Assert.True(Rgba32.HotPink == sourcePixels[11, 11], "[11, 11] wrong");
Assert.Equal(Rgba32.HotPink, sourcePixels[199, 150]); Assert.True(Rgba32.HotPink == sourcePixels[199, 149], "[199, 149] wrong");
Assert.Equal(Rgba32.HotPink, sourcePixels[50, 50]); Assert.True(Rgba32.HotPink == sourcePixels[50, 50], "[50, 50] wrong");
Assert.Equal(Rgba32.Blue, sourcePixels[2, 2]); Assert.True(Rgba32.Blue == sourcePixels[2, 2], "[2, 2] wrong");
} }
} }
} }

42
tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs

@ -11,6 +11,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests
{ {
using System.IO; using System.IO;
using SixLabors.ImageSharp.Advanced;
public class GifDecoderTests public class GifDecoderTests
{ {
@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests
public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans };
public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 };
[Theory] [Theory]
[WithFileCollection(nameof(TestFiles), PixelTypes)] [WithFileCollection(nameof(TestFiles), PixelTypes)]
public void DecodeAndReSave<TPixel>(TestImageProvider<TPixel> imageProvider) public void DecodeAndReSave<TPixel>(TestImageProvider<TPixel> imageProvider)
@ -29,6 +32,7 @@ namespace SixLabors.ImageSharp.Tests
imageProvider.Utility.SaveTestOutputFile(image, "gif"); imageProvider.Utility.SaveTestOutputFile(image, "gif");
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(TestFiles), PixelTypes)] [WithFileCollection(nameof(TestFiles), PixelTypes)]
public void DecodeResizeAndSave<TPixel>(TestImageProvider<TPixel> imageProvider) public void DecodeResizeAndSave<TPixel>(TestImageProvider<TPixel> imageProvider)
@ -129,5 +133,43 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel); Assert.Equal(expectedPixelSize, Image.DetectPixelType(stream)?.BitsPerPixel);
} }
} }
[Fact]
public void CanDecodeIntermingledImages()
{
using (var kumin1 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes))
using (var icon = Image.Load(TestFile.Create(TestImages.Png.Icon).Bytes))
using (var kumin2 = Image.Load(TestFile.Create(TestImages.Gif.Kumin).Bytes))
{
for (int i = 0; i < kumin1.Frames.Count; i++)
{
ImageFrame<Rgba32> first = kumin1.Frames[i];
ImageFrame<Rgba32> second = kumin2.Frames[i];
first.ComparePixelBufferTo(second.GetPixelSpan());
}
}
}
[Theory]
[WithFileCollection(nameof(BadAppExtFiles), PixelTypes.Rgba32)]
public void DecodeBadApplicationExtensionLength<TPixel>(TestImageProvider<TPixel> imageProvider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = imageProvider.GetImage())
{
imageProvider.Utility.SaveTestOutputFile(image, "bmp");
}
}
[Theory]
[WithFile(TestImages.Gif.Issues.BadDescriptorWidth, PixelTypes.Rgba32)]
public void DecodeBadDescriptorDimensionsLength<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
provider.Utility.SaveTestOutputFile(image, "bmp");
}
}
} }
} }

1
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs

@ -42,6 +42,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Baseline.Jpeg444, TestImages.Jpeg.Baseline.Jpeg444,
TestImages.Jpeg.Baseline.Bad.BadEOF, TestImages.Jpeg.Baseline.Bad.BadEOF,
TestImages.Jpeg.Baseline.Bad.ExifUndefType, TestImages.Jpeg.Baseline.Bad.ExifUndefType,
TestImages.Jpeg.Issues.MultiHuffmanBaseline394,
}; };
public static string[] ProgressiveTestJpegs = public static string[] ProgressiveTestJpegs =

58
tests/ImageSharp.Tests/Issues/Issue412.cs

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text;
using SixLabors.Primitives;
using SixLabors.ImageSharp.Advanced;
using Xunit;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.Issues
{
public class Issue412
{
[Theory]
[WithBlankImages(40, 30, PixelTypes.Rgba32)]
public void AllPixelsExpectedToBeRedWhenAntialisedDisabled<TPixel>(TestImageProvider<TPixel> provider) where TPixel : struct, IPixel<TPixel>
{
using (var image = provider.GetImage())
{
image.Mutate(
context =>
{
for (var i = 0; i < 40; ++i)
{
context.DrawLines(
NamedColors<TPixel>.Black,
1,
new[]
{
new PointF(i, 0.1066f),
new PointF(i, 10.1066f)
},
new GraphicsOptions(true));
context.DrawLines(
NamedColors<TPixel>.Red,
1,
new[]
{
new PointF(i, 15.1066f),
new PointF(i, 25.1066f)
},
new GraphicsOptions(false));
}
});
image.DebugSave(provider);
for (var y = 15; y < 25; y++)
{
for (var x = 0; x < 40; x++)
{
Assert.True(NamedColors<TPixel>.Red.Equals(image[x, y]), $"expected {NamedColors<TPixel>.Red} but found {image[x, y]} at [{x}, {y}]");
}
}
}
}
}
}

12
tests/ImageSharp.Tests/TestImages.cs

@ -34,6 +34,7 @@ namespace SixLabors.ImageSharp.Tests
public const string BikeGrayscale = "Png/BikeGrayscale.png"; public const string BikeGrayscale = "Png/BikeGrayscale.png";
public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png"; public const string Rgb48BppInterlaced = "Png/rgb-48bpp-interlaced.png";
public const string SnakeGame = "Png/SnakeGame.png"; public const string SnakeGame = "Png/SnakeGame.png";
public const string Icon = "Png/icon.png";
// Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html // Filtered test images from http://www.schaik.com/pngsuite/pngsuite_fil_png.html
public const string Filter0 = "Png/filter0.png"; public const string Filter0 = "Png/filter0.png";
@ -126,6 +127,7 @@ namespace SixLabors.ImageSharp.Tests
public const string MissingFF00ProgressiveGirl159 = "Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg"; public const string MissingFF00ProgressiveGirl159 = "Jpg/issues/Issue159-MissingFF00-Progressive-Girl.jpg";
public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg"; public const string BadCoeffsProgressive178 = "Jpg/issues/Issue178-BadCoeffsProgressive-Lemon.jpg";
public const string BadZigZagProgressive385 = "Jpg/issues/Issue385-BadZigZag-Progressive.jpg"; public const string BadZigZagProgressive385 = "Jpg/issues/Issue385-BadZigZag-Progressive.jpg";
public const string MultiHuffmanBaseline394 = "Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg";
} }
public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray(); public static readonly string[] All = Baseline.All.Concat(Progressive.All).ToArray();
@ -156,8 +158,16 @@ namespace SixLabors.ImageSharp.Tests
public const string Giphy = "Gif/giphy.gif"; public const string Giphy = "Gif/giphy.gif";
public const string Cheers = "Gif/cheers.gif"; public const string Cheers = "Gif/cheers.gif";
public const string Trans = "Gif/trans.gif"; public const string Trans = "Gif/trans.gif";
public const string Kumin = "Gif/kumin.gif";
public static readonly string[] All = { Rings, Giphy, Cheers, Trans }; public class Issues
{
public const string BadAppExtLength = "Gif/issues/issue405_badappextlength252.gif";
public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif";
public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif";
}
public static readonly string[] All = { Rings, Giphy, Cheers, Trans, Kumin };
} }
} }
} }

1
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -29,7 +29,6 @@ namespace SixLabors.ImageSharp.Tests
/// <param name="provider">The image provider</param> /// <param name="provider">The image provider</param>
/// <param name="testOutputDetails">Details to be concatenated to the test output file, describing the parameters of the test.</param> /// <param name="testOutputDetails">Details to be concatenated to the test output file, describing the parameters of the test.</param>
/// <param name="extension">The extension</param> /// <param name="extension">The extension</param>
/// <param name="grayscale">A boolean indicating whether we should save a smaller in size.</param>
/// <param name="appendPixelTypeToFileName">A boolean indicating whether to append the pixel type to the output file name.</param> /// <param name="appendPixelTypeToFileName">A boolean indicating whether to append the pixel type to the output file name.</param>
public static Image<TPixel> DebugSave<TPixel>( public static Image<TPixel> DebugSave<TPixel>(
this Image<TPixel> image, this Image<TPixel> image,

2
tests/Images/External

@ -1 +1 @@
Subproject commit ab7c90362a4881c790c6f264f400dcf4b4dea0ce Subproject commit dc5479d00b2312f691e6249b9f7765e2316d4a30

BIN
tests/Images/Input/Gif/issues/issue403_baddescriptorwidth.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 KiB

BIN
tests/Images/Input/Gif/issues/issue405_badappextlength252-2.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
tests/Images/Input/Gif/issues/issue405_badappextlength252.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
tests/Images/Input/Gif/kumin.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

BIN
tests/Images/Input/Jpg/issues/Issue394-MultiHuffmanBaseline-Speakers.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

BIN
tests/Images/Input/Png/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Loading…
Cancel
Save