Skip to content

Commit

Permalink
Textures: Automatic resizing
Browse files Browse the repository at this point in the history
  • Loading branch information
Exter-N committed Aug 26, 2023
1 parent 792707a commit 99b43bf
Showing 1 changed file with 106 additions and 27 deletions.
133 changes: 106 additions & 27 deletions Penumbra/Import/Textures/CombinedTexture.Manipulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using SixLabors.ImageSharp.PixelFormats;
using Dalamud.Interface;
using Penumbra.UI;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

namespace Penumbra.Import.Textures;

Expand All @@ -25,6 +27,13 @@ private enum CombineOp
CopyChannels = 3,
}

private enum ResizeOp
{
None = 0,
ToLeft = 1,
ToRight = 2,
}

[Flags]
private enum Channels
{
Expand All @@ -41,8 +50,16 @@ private enum Channels
private int _offsetX = 0;
private int _offsetY = 0;
private CombineOp _combineOp = CombineOp.Over;
private ResizeOp _resizeOp = ResizeOp.None;
private Channels _copyChannels = Channels.Red | Channels.Green | Channels.Blue | Channels.Alpha;

private int _rightWidth = 0;
private int _rightHeight = 0;
private int _targetWidth = 0;
private int _targetHeight = 0;
private byte[] _leftPixels = Array.Empty<byte>();
private byte[] _rightPixels = Array.Empty<byte>();

private static readonly IReadOnlyList<string> CombineOpLabels = new string[]
{
"Overlay over Input",
Expand All @@ -59,6 +76,26 @@ private enum Channels
"Replace some input channels with those from the overlay.\nUseful for Multi maps.",
};

private static (bool UsesLeft, bool UsesRight) GetCombineOpFlags(CombineOp combineOp)
=> combineOp switch
{
CombineOp.LeftCopy => (true, false),
CombineOp.LeftMultiply => (true, false),
CombineOp.RightCopy => (false, true),
CombineOp.RightMultiply => (false, true),
CombineOp.Over => (true, true),
CombineOp.Under => (true, true),
CombineOp.CopyChannels => (true, true),
_ => throw new ArgumentException($"Invalid combine operation {combineOp}"),
};

private static readonly IReadOnlyList<string> ResizeOpLabels = new string[]
{
"No Resizing",
"Adjust Overlay to Input",
"Adjust Input to Overlay",
};

private const float OneThird = 1.0f / 3.0f;
private const float RWeight = 0.2126f;
private const float GWeight = 0.7152f;
Expand Down Expand Up @@ -107,27 +144,27 @@ private CombineOp GetActualCombineOp()
}

private Vector4 DataLeft(int offset)
=> CappedVector(_left.RgbaPixels, offset, _multiplierLeft, _constantLeft);
=> CappedVector(_leftPixels, offset, _multiplierLeft, _constantLeft);

private Vector4 DataRight(int offset)
=> CappedVector(_right.RgbaPixels, offset, _multiplierRight, _constantRight);
=> CappedVector(_rightPixels, offset, _multiplierRight, _constantRight);

private Vector4 DataRight(int x, int y)
{
x += _offsetX;
y += _offsetY;
if (x < 0 || x >= _right.TextureWrap!.Width || y < 0 || y >= _right.TextureWrap!.Height)
if (x < 0 || x >= _rightWidth || y < 0 || y >= _rightHeight)
return Vector4.Zero;

var offset = (y * _right.TextureWrap!.Width + x) * 4;
return CappedVector(_right.RgbaPixels, offset, _multiplierRight, _constantRight);
var offset = (y * _rightWidth + x) * 4;
return CappedVector(_rightPixels, offset, _multiplierRight, _constantRight);
}

private void AddPixelsMultiplied(int y, ParallelLoopState _)
{
for (var x = 0; x < _left.TextureWrap!.Width; ++x)
for (var x = 0; x < _targetWidth; ++x)
{
var offset = (_left.TextureWrap!.Width * y + x) * 4;
var offset = (_targetWidth * y + x) * 4;
var left = DataLeft(offset);
var right = DataRight(x, y);
var alpha = right.W + left.W * (1 - right.W);
Expand All @@ -143,9 +180,9 @@ private void AddPixelsMultiplied(int y, ParallelLoopState _)

private void ReverseAddPixelsMultiplied(int y, ParallelLoopState _)
{
for (var x = 0; x < _left.TextureWrap!.Width; ++x)
for (var x = 0; x < _targetWidth; ++x)
{
var offset = (_left.TextureWrap!.Width * y + x) * 4;
var offset = (_targetWidth * y + x) * 4;
var left = DataLeft(offset);
var right = DataRight(x, y);
var alpha = left.W + right.W * (1 - left.W);
Expand All @@ -162,9 +199,9 @@ private void ReverseAddPixelsMultiplied(int y, ParallelLoopState _)
private void ChannelMergePixelsMultiplied(int y, ParallelLoopState _)
{
var channels = _copyChannels;
for (var x = 0; x < _left.TextureWrap!.Width; ++x)
for (var x = 0; x < _targetWidth; ++x)
{
var offset = (_left.TextureWrap!.Width * y + x) * 4;
var offset = (_targetWidth * y + x) * 4;
var left = DataLeft(offset);
var right = DataRight(x, y);
var rgba = new Rgba32((channels & Channels.Red) != 0 ? right.X : left.X,
Expand All @@ -180,9 +217,9 @@ private void ChannelMergePixelsMultiplied(int y, ParallelLoopState _)

private void MultiplyPixelsLeft(int y, ParallelLoopState _)
{
for (var x = 0; x < _left.TextureWrap!.Width; ++x)
for (var x = 0; x < _targetWidth; ++x)
{
var offset = (_left.TextureWrap!.Width * y + x) * 4;
var offset = (_targetWidth * y + x) * 4;
var left = DataLeft(offset);
var rgba = new Rgba32(left);
_centerStorage.RgbaPixels[offset] = rgba.R;
Expand All @@ -194,9 +231,9 @@ private void MultiplyPixelsLeft(int y, ParallelLoopState _)

private void MultiplyPixelsRight(int y, ParallelLoopState _)
{
for (var x = 0; x < _right.TextureWrap!.Width; ++x)
for (var x = 0; x < _targetWidth; ++x)
{
var offset = (_right.TextureWrap!.Width * y + x) * 4;
var offset = (_targetWidth * y + x) * 4;
var right = DataRight(offset);
var rgba = new Rgba32(right);
_centerStorage.RgbaPixels[offset] = rgba.R;
Expand All @@ -206,26 +243,59 @@ private void MultiplyPixelsRight(int y, ParallelLoopState _)
}
}

private byte[] ResizePixels(byte[] rgbaPixels, int sourceWidth, int sourceHeight)
{
if (sourceWidth == _targetWidth && sourceHeight == _targetHeight)
return rgbaPixels;

byte[] resizedPixels;
using (var image = Image.LoadPixelData<Rgba32>(rgbaPixels, sourceWidth, sourceHeight))
{
image.Mutate(ctx => ctx.Resize(_targetWidth, _targetHeight));

resizedPixels = new byte[_targetWidth * _targetHeight * 4];
image.CopyPixelDataTo(resizedPixels);
}

return resizedPixels;
}


private (int Width, int Height) CombineImage()
{
var combineOp = GetActualCombineOp();
var (width, height) = combineOp is not CombineOp.Invalid or CombineOp.RightCopy or CombineOp.RightMultiply
var (usesLeft, usesRight) = GetCombineOpFlags(combineOp);
var resizeOp = usesLeft && usesRight ? _resizeOp : ResizeOp.None;
(_targetWidth, _targetHeight) = usesLeft && resizeOp != ResizeOp.ToRight
? (_left.TextureWrap!.Width, _left.TextureWrap!.Height)
: (_right.TextureWrap!.Width, _right.TextureWrap!.Height);
_centerStorage.RgbaPixels = new byte[width * height * 4];
_centerStorage.RgbaPixels = new byte[_targetWidth * _targetHeight * 4];
_centerStorage.Type = TextureType.Bitmap;
Parallel.For(0, height, combineOp switch
try
{
if (usesLeft)
_leftPixels = (resizeOp == ResizeOp.ToRight) ? ResizePixels(_left.RgbaPixels, _left.TextureWrap!.Width, _left.TextureWrap!.Height) : _left.RgbaPixels;
if (usesRight)
(_rightWidth, _rightHeight, _rightPixels) = (resizeOp == ResizeOp.ToLeft)
? (_targetWidth, _targetHeight, ResizePixels(_right.RgbaPixels, _right.TextureWrap!.Width, _right.TextureWrap!.Height))
: (_right.TextureWrap!.Width, _right.TextureWrap!.Height, _right.RgbaPixels);
Parallel.For(0, _targetHeight, combineOp switch
{
CombineOp.Over => AddPixelsMultiplied,
CombineOp.Under => ReverseAddPixelsMultiplied,
CombineOp.LeftMultiply => MultiplyPixelsLeft,
CombineOp.RightMultiply => MultiplyPixelsRight,
CombineOp.CopyChannels => ChannelMergePixelsMultiplied,
_ => throw new InvalidOperationException($"Cannot combine images with operation {combineOp}"),
});
}
finally
{
CombineOp.Over => AddPixelsMultiplied,
CombineOp.Under => ReverseAddPixelsMultiplied,
CombineOp.LeftMultiply => MultiplyPixelsLeft,
CombineOp.RightMultiply => MultiplyPixelsRight,
CombineOp.CopyChannels => ChannelMergePixelsMultiplied,
_ => throw new InvalidOperationException($"Cannot combine images with operation {combineOp}"),
});

return (width, height);
_leftPixels = Array.Empty<byte>();
_rightPixels = Array.Empty<byte>();
}

return (_targetWidth, _targetHeight);
}

private static Vector4 CappedVector(IReadOnlyList<byte> bytes, int offset, Matrix4x4 transform, Vector4 constant)
Expand Down Expand Up @@ -266,13 +336,15 @@ public void DrawMatrixInputRight(float width)
{
var ret = DrawMatrixInput(ref _multiplierRight, ref _constantRight, width);
ret |= DrawMatrixTools(ref _multiplierRight, ref _constantRight);

ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
ImGui.DragInt("##XOffset", ref _offsetX, 0.5f);
ret |= ImGui.IsItemDeactivatedAfterEdit();
ImGui.SameLine();
ImGui.SetNextItemWidth(75.0f * UiHelpers.Scale);
ImGui.DragInt("Offsets##YOffset", ref _offsetY, 0.5f);
ret |= ImGui.IsItemDeactivatedAfterEdit();

ImGui.SetNextItemWidth(200.0f * UiHelpers.Scale);
using (var c = ImRaii.Combo("Combine Operation", CombineOpLabels[(int)_combineOp]))
{
Expand All @@ -292,6 +364,13 @@ public void DrawMatrixInputRight(float width)
}
}

var (usesLeft, usesRight) = GetCombineOpFlags(_combineOp);
using (var dis = ImRaii.Disabled(!usesLeft || !usesRight))
{
ret |= ImGuiUtil.GenericEnumCombo("Resizing Mode", 200.0f * UiHelpers.Scale, _resizeOp, out _resizeOp,
Enum.GetValues<ResizeOp>(), op => ResizeOpLabels[(int)op]);
}

using (var dis = ImRaii.Disabled(_combineOp != CombineOp.CopyChannels))
{
ImGui.TextUnformatted("Copy");
Expand Down

0 comments on commit 99b43bf

Please sign in to comment.