mirror of https://github.com/SixLabors/ImageSharp
11 changed files with 349 additions and 89 deletions
@ -0,0 +1,129 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Webp.Lossless; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.WebP.Lossless |
|||
{ |
|||
/// <summary>
|
|||
/// Near-lossless image preprocessing adjusts pixel values to help compressibility with a guarantee
|
|||
/// of maximum deviation between original and resulting pixel values.
|
|||
/// </summary>
|
|||
internal static class NearLosslessEnc |
|||
{ |
|||
private const int MinDimForNearLossless = 64; |
|||
|
|||
public static void ApplyNearLossless(int xSize, int ySize, int quality, Span<uint> argbSrc, Span<uint> argbDst, int stride) |
|||
{ |
|||
uint[] copyBuffer = new uint[xSize * 3]; |
|||
int limitBits = LosslessUtils.NearLosslessBits(quality); |
|||
|
|||
// For small icon images, don't attempt to apply near-lossless compression.
|
|||
if ((xSize < MinDimForNearLossless && ySize < MinDimForNearLossless) || ySize < 3) |
|||
{ |
|||
for (int i = 0; i < ySize; ++i) |
|||
{ |
|||
argbSrc.Slice(i * stride, xSize).CopyTo(argbDst.Slice(i * xSize, xSize)); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
NearLossless(xSize, ySize, argbSrc, stride, limitBits, copyBuffer, argbDst); |
|||
for (int i = limitBits - 1; i != 0; --i) |
|||
{ |
|||
NearLossless(xSize, ySize, argbDst, xSize, i, copyBuffer, argbDst); |
|||
} |
|||
} |
|||
|
|||
// Adjusts pixel values of image with given maximum error.
|
|||
private static void NearLossless(int xSize, int ySize, Span<uint> argbSrc, int stride, int limitBits, Span<uint> copyBuffer, Span<uint> argbDst) |
|||
{ |
|||
int x, y; |
|||
int limit = 1 << limitBits; |
|||
Span<uint> prevRow = copyBuffer; |
|||
Span<uint> currRow = copyBuffer.Slice(xSize, xSize); |
|||
Span<uint> nextRow = copyBuffer.Slice(xSize * 2, xSize); |
|||
argbSrc.Slice(0, xSize).CopyTo(currRow); |
|||
argbSrc.Slice(xSize, xSize).CopyTo(nextRow); |
|||
|
|||
int srcOffset = 0; |
|||
int dstOffset = 0; |
|||
for (y = 0; y < ySize; ++y) |
|||
{ |
|||
if (y == 0 || y == ySize - 1) |
|||
{ |
|||
argbSrc.Slice(srcOffset, xSize).CopyTo(argbDst.Slice(dstOffset, xSize)); |
|||
} |
|||
else |
|||
{ |
|||
argbSrc.Slice(srcOffset + stride, xSize).CopyTo(nextRow); |
|||
argbDst[dstOffset] = argbSrc[srcOffset]; |
|||
argbDst[dstOffset + xSize - 1] = argbSrc[srcOffset + xSize - 1]; |
|||
for (x = 1; x < xSize - 1; ++x) |
|||
{ |
|||
if (IsSmooth(prevRow, currRow, nextRow, x, limit)) |
|||
{ |
|||
argbDst[dstOffset + x] = currRow[x]; |
|||
} |
|||
else |
|||
{ |
|||
argbDst[dstOffset + x] = ClosestDiscretizedArgb(currRow[x], limitBits); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Span<uint> temp = prevRow; |
|||
prevRow = currRow; |
|||
currRow = nextRow; |
|||
nextRow = temp; |
|||
srcOffset += stride; |
|||
dstOffset += xSize; |
|||
} |
|||
} |
|||
|
|||
// Applies FindClosestDiscretized to all channels of pixel.
|
|||
private static uint ClosestDiscretizedArgb(uint a, int bits) => |
|||
(FindClosestDiscretized(a >> 24, bits) << 24) | |
|||
(FindClosestDiscretized((a >> 16) & 0xff, bits) << 16) | |
|||
(FindClosestDiscretized((a >> 8) & 0xff, bits) << 8) | |
|||
FindClosestDiscretized(a & 0xff, bits); |
|||
|
|||
private static uint FindClosestDiscretized(uint a, int bits) |
|||
{ |
|||
uint mask = (1u << bits) - 1; |
|||
uint biased = a + (mask >> 1) + ((a >> bits) & 1); |
|||
if (biased > 0xff) |
|||
{ |
|||
return 0xff; |
|||
} |
|||
|
|||
return biased & ~mask; |
|||
} |
|||
|
|||
private static bool IsSmooth(Span<uint> prevRow, Span<uint> currRow, Span<uint> nextRow, int ix, int limit) |
|||
{ |
|||
// Check that all pixels in 4-connected neighborhood are smooth.
|
|||
return IsNear(currRow[ix], currRow[ix - 1], limit) && |
|||
IsNear(currRow[ix], currRow[ix + 1], limit) && |
|||
IsNear(currRow[ix], prevRow[ix], limit) && |
|||
IsNear(currRow[ix], nextRow[ix], limit); |
|||
} |
|||
|
|||
// Checks if distance between corresponding channel values of pixels a and b is within the given limit.
|
|||
private static bool IsNear(uint a, uint b, int limit) |
|||
{ |
|||
for (int k = 0; k < 4; ++k) |
|||
{ |
|||
int delta = (int)((a >> (k * 8)) & 0xff) - (int)((b >> (k * 8)) & 0xff); |
|||
if (delta >= limit || delta <= -limit) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:f1ffed99a3cc701fff7f63cdca32c437a3e03d2a8a178380744190636decb0f8 |
|||
size 12453 |
|||
Loading…
Reference in new issue