mirror of https://github.com/SixLabors/ImageSharp
9 changed files with 270 additions and 320 deletions
@ -0,0 +1,162 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
|
||||
|
// ReSharper disable UseObjectOrCollectionInitializer
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
||||
|
{ |
||||
|
public partial struct Block8x8F |
||||
|
{ |
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void ScaledCopyFrom(ref float areaOrigin, int areaStride) => |
||||
|
CopyFrom1x1Scale(ref Unsafe.As<float, byte>(ref areaOrigin), ref Unsafe.As<Block8x8F, byte>(ref this), areaStride); |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
||||
|
{ |
||||
|
if (horizontalScale == 1 && verticalScale == 1) |
||||
|
{ |
||||
|
CopyTo1x1Scale(ref Unsafe.As<Block8x8F, byte>(ref this), ref Unsafe.As<float, byte>(ref areaOrigin), areaStride); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (horizontalScale == 2 && verticalScale == 2) |
||||
|
{ |
||||
|
this.CopyTo2x2Scale(ref areaOrigin, areaStride); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
||||
|
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
||||
|
} |
||||
|
|
||||
|
private void CopyTo2x2Scale(ref float areaOrigin, int areaStride) |
||||
|
{ |
||||
|
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
||||
|
int destStride = areaStride / 2; |
||||
|
|
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 0, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 1, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 2, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 3, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 4, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 5, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 6, destStride); |
||||
|
WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 7, destStride); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, int row, int destStride) |
||||
|
{ |
||||
|
ref Vector4 sLeft = ref Unsafe.Add(ref selfBase, 2 * row); |
||||
|
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
||||
|
|
||||
|
int offset = 2 * row * destStride; |
||||
|
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
||||
|
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
||||
|
|
||||
|
var xyLeft = new Vector4(sLeft.X); |
||||
|
xyLeft.Z = sLeft.Y; |
||||
|
xyLeft.W = sLeft.Y; |
||||
|
|
||||
|
var zwLeft = new Vector4(sLeft.Z); |
||||
|
zwLeft.Z = sLeft.W; |
||||
|
zwLeft.W = sLeft.W; |
||||
|
|
||||
|
var xyRight = new Vector4(sRight.X); |
||||
|
xyRight.Z = sRight.Y; |
||||
|
xyRight.W = sRight.Y; |
||||
|
|
||||
|
var zwRight = new Vector4(sRight.Z); |
||||
|
zwRight.Z = sRight.W; |
||||
|
zwRight.W = sRight.W; |
||||
|
|
||||
|
dTopLeft = xyLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
||||
|
|
||||
|
dBottomLeft = xyLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
||||
|
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
||||
|
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(InliningOptions.ColdPath)] |
||||
|
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
||||
|
{ |
||||
|
for (int y = 0; y < 8; y++) |
||||
|
{ |
||||
|
int yy = y * verticalScale; |
||||
|
int y8 = y * 8; |
||||
|
|
||||
|
for (int x = 0; x < 8; x++) |
||||
|
{ |
||||
|
int xx = x * horizontalScale; |
||||
|
|
||||
|
float value = this[y8 + x]; |
||||
|
|
||||
|
for (int i = 0; i < verticalScale; i++) |
||||
|
{ |
||||
|
int baseIdx = ((yy + i) * areaStride) + xx; |
||||
|
|
||||
|
for (int j = 0; j < horizontalScale; j++) |
||||
|
{ |
||||
|
// area[xx + j, yy + i] = value;
|
||||
|
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CopyTo1x1Scale(ref byte origin, ref byte dest, int areaStride) |
||||
|
{ |
||||
|
int destStride = areaStride * sizeof(float); |
||||
|
|
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 0); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 1); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 2); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 3); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 4); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 5); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 6); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 7); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void CopyRowImpl(ref byte origin, ref byte dest, int destStride, int row) |
||||
|
{ |
||||
|
origin = ref Unsafe.Add(ref origin, row * 8 * sizeof(float)); |
||||
|
dest = ref Unsafe.Add(ref dest, row * destStride); |
||||
|
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void CopyFrom1x1Scale(ref byte origin, ref byte dest, int areaStride) |
||||
|
{ |
||||
|
int destStride = areaStride * sizeof(float); |
||||
|
|
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 0); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 1); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 2); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 3); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 4); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 5); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 6); |
||||
|
CopyRowImpl(ref origin, ref dest, destStride, 7); |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
static void CopyRowImpl(ref byte origin, ref byte dest, int sourceStride, int row) |
||||
|
{ |
||||
|
origin = ref Unsafe.Add(ref origin, row * sourceStride); |
||||
|
dest = ref Unsafe.Add(ref dest, row * 8 * sizeof(float)); |
||||
|
Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,179 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
|
|
||||
// ReSharper disable UseObjectOrCollectionInitializer
|
|
||||
// ReSharper disable InconsistentNaming
|
|
||||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|
||||
{ |
|
||||
public partial struct Block8x8F |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
|
|
||||
/// </summary>
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void ScaledCopyTo(in Buffer2DRegion<float> region, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
ref float areaOrigin = ref region.GetReferenceToOrigin(); |
|
||||
this.ScaledCopyTo(ref areaOrigin, region.Stride, horizontalScale, verticalScale); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void ScaledCopyFrom(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
if (horizontalScale == 1 && verticalScale == 1) |
|
||||
{ |
|
||||
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); |
|
||||
ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin); |
|
||||
int destStride = areaStride * sizeof(float); |
|
||||
|
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 0); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 1); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 2); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 3); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 4); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 5); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 6); |
|
||||
CopyRowImplFromStrides(ref selfBase, ref destBase, destStride, 7); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
throw new NotImplementedException("This is a test setup!"); |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
static void CopyRowImplFromStrides(ref byte src, ref byte dst, int srcStride, int row) |
|
||||
{ |
|
||||
ref byte s = ref Unsafe.Add(ref dst, row * srcStride); |
|
||||
ref byte d = ref Unsafe.Add(ref src, row * 8 * sizeof(float)); |
|
||||
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ShortMethod)] |
|
||||
public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
if (horizontalScale == 1 && verticalScale == 1) |
|
||||
{ |
|
||||
this.Copy1x1Scale(ref areaOrigin, areaStride); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (horizontalScale == 2 && verticalScale == 2) |
|
||||
{ |
|
||||
this.Copy2x2Scale(ref areaOrigin, areaStride); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// TODO: Optimize: implement all cases with scale-specific, loopless code!
|
|
||||
this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale); |
|
||||
} |
|
||||
|
|
||||
public void Copy1x1Scale(ref float areaOrigin, int areaStride) |
|
||||
{ |
|
||||
ref byte selfBase = ref Unsafe.As<Block8x8F, byte>(ref this); |
|
||||
ref byte destBase = ref Unsafe.As<float, byte>(ref areaOrigin); |
|
||||
int destStride = areaStride * sizeof(float); |
|
||||
|
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 0); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 1); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 2); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 3); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 4); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 5); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 6); |
|
||||
CopyRowImpl(ref selfBase, ref destBase, destStride, 7); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row) |
|
||||
{ |
|
||||
ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float)); |
|
||||
ref byte d = ref Unsafe.Add(ref destBase, row * destStride); |
|
||||
Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float)); |
|
||||
} |
|
||||
|
|
||||
private void Copy2x2Scale(ref float areaOrigin, int areaStride) |
|
||||
{ |
|
||||
ref Vector2 destBase = ref Unsafe.As<float, Vector2>(ref areaOrigin); |
|
||||
int destStride = areaStride / 2; |
|
||||
|
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 0, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 1, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 2, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 3, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 4, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 5, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 6, destStride); |
|
||||
this.WidenCopyRowImpl2x2(ref destBase, 7, destStride); |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride) |
|
||||
{ |
|
||||
ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row); |
|
||||
ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1); |
|
||||
|
|
||||
int offset = 2 * row * destStride; |
|
||||
ref Vector4 dTopLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset)); |
|
||||
ref Vector4 dBottomLeft = ref Unsafe.As<Vector2, Vector4>(ref Unsafe.Add(ref destBase, offset + destStride)); |
|
||||
|
|
||||
var xyLeft = new Vector4(sLeft.X); |
|
||||
xyLeft.Z = sLeft.Y; |
|
||||
xyLeft.W = sLeft.Y; |
|
||||
|
|
||||
var zwLeft = new Vector4(sLeft.Z); |
|
||||
zwLeft.Z = sLeft.W; |
|
||||
zwLeft.W = sLeft.W; |
|
||||
|
|
||||
var xyRight = new Vector4(sRight.X); |
|
||||
xyRight.Z = sRight.Y; |
|
||||
xyRight.W = sRight.Y; |
|
||||
|
|
||||
var zwRight = new Vector4(sRight.Z); |
|
||||
zwRight.Z = sRight.W; |
|
||||
zwRight.W = sRight.W; |
|
||||
|
|
||||
dTopLeft = xyLeft; |
|
||||
Unsafe.Add(ref dTopLeft, 1) = zwLeft; |
|
||||
Unsafe.Add(ref dTopLeft, 2) = xyRight; |
|
||||
Unsafe.Add(ref dTopLeft, 3) = zwRight; |
|
||||
|
|
||||
dBottomLeft = xyLeft; |
|
||||
Unsafe.Add(ref dBottomLeft, 1) = zwLeft; |
|
||||
Unsafe.Add(ref dBottomLeft, 2) = xyRight; |
|
||||
Unsafe.Add(ref dBottomLeft, 3) = zwRight; |
|
||||
} |
|
||||
|
|
||||
[MethodImpl(InliningOptions.ColdPath)] |
|
||||
private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale) |
|
||||
{ |
|
||||
for (int y = 0; y < 8; y++) |
|
||||
{ |
|
||||
int yy = y * verticalScale; |
|
||||
int y8 = y * 8; |
|
||||
|
|
||||
for (int x = 0; x < 8; x++) |
|
||||
{ |
|
||||
int xx = x * horizontalScale; |
|
||||
|
|
||||
float value = this[y8 + x]; |
|
||||
|
|
||||
for (int i = 0; i < verticalScale; i++) |
|
||||
{ |
|
||||
int baseIdx = ((yy + i) * areaStride) + xx; |
|
||||
|
|
||||
for (int j = 0; j < horizontalScale; j++) |
|
||||
{ |
|
||||
// area[xx + j, yy + i] = value;
|
|
||||
Unsafe.Add(ref areaOrigin, baseIdx + j) = value; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,97 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
// Uncomment this to turn unit tests into benchmarks:
|
|
||||
// #define BENCHMARKING
|
|
||||
using SixLabors.ImageSharp.Formats.Jpeg.Components; |
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; |
|
||||
|
|
||||
using Xunit; |
|
||||
using Xunit.Abstractions; |
|
||||
|
|
||||
// ReSharper disable InconsistentNaming
|
|
||||
namespace SixLabors.ImageSharp.Tests.Formats.Jpg |
|
||||
{ |
|
||||
[Trait("Format", "Jpg")] |
|
||||
public partial class Block8x8FTests |
|
||||
{ |
|
||||
public class CopyToBufferArea : JpegFixture |
|
||||
{ |
|
||||
public CopyToBufferArea(ITestOutputHelper output) |
|
||||
: base(output) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
private static void VerifyAllZeroOutsideSubArea(Buffer2D<float> buffer, int subX, int subY, int horizontalFactor = 1, int verticalFactor = 1) |
|
||||
{ |
|
||||
for (int y = 0; y < 20; y++) |
|
||||
{ |
|
||||
for (int x = 0; x < 20; x++) |
|
||||
{ |
|
||||
if (x < subX || x >= subX + (8 * horizontalFactor) || y < subY || y >= subY + (8 * verticalFactor)) |
|
||||
{ |
|
||||
Assert.Equal(0, buffer[x, y]); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Copy1x1Scale() |
|
||||
{ |
|
||||
Block8x8F block = CreateRandomFloatBlock(0, 100); |
|
||||
|
|
||||
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(20, 20, AllocationOptions.Clean)) |
|
||||
{ |
|
||||
Buffer2DRegion<float> region = buffer.GetRegion(5, 10, 8, 8); |
|
||||
block.Copy1x1Scale(ref region.GetReferenceToOrigin(), region.Stride); |
|
||||
|
|
||||
Assert.Equal(block[0, 0], buffer[5, 10]); |
|
||||
Assert.Equal(block[1, 0], buffer[6, 10]); |
|
||||
Assert.Equal(block[0, 1], buffer[5, 11]); |
|
||||
Assert.Equal(block[0, 7], buffer[5, 17]); |
|
||||
Assert.Equal(block[63], buffer[12, 17]); |
|
||||
|
|
||||
VerifyAllZeroOutsideSubArea(buffer, 5, 10); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Theory] |
|
||||
[InlineData(1, 1)] |
|
||||
[InlineData(1, 2)] |
|
||||
[InlineData(2, 1)] |
|
||||
[InlineData(2, 2)] |
|
||||
[InlineData(4, 2)] |
|
||||
[InlineData(4, 4)] |
|
||||
public void CopyTo(int horizontalFactor, int verticalFactor) |
|
||||
{ |
|
||||
Block8x8F block = CreateRandomFloatBlock(0, 100); |
|
||||
|
|
||||
var start = new Point(50, 50); |
|
||||
|
|
||||
using (Buffer2D<float> buffer = Configuration.Default.MemoryAllocator.Allocate2D<float>(100, 100, AllocationOptions.Clean)) |
|
||||
{ |
|
||||
Buffer2DRegion<float> region = buffer.GetRegion(start.X, start.Y, 8 * horizontalFactor, 8 * verticalFactor); |
|
||||
block.ScaledCopyTo(region, horizontalFactor, verticalFactor); |
|
||||
|
|
||||
for (int y = 0; y < 8 * verticalFactor; y++) |
|
||||
{ |
|
||||
for (int x = 0; x < 8 * horizontalFactor; x++) |
|
||||
{ |
|
||||
int yy = y / verticalFactor; |
|
||||
int xx = x / horizontalFactor; |
|
||||
|
|
||||
float expected = block[xx, yy]; |
|
||||
float actual = region[x, y]; |
|
||||
|
|
||||
Assert.Equal(expected, actual); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
VerifyAllZeroOutsideSubArea(buffer, start.X, start.Y, horizontalFactor, verticalFactor); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue