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