/ src / Ryujinx.Graphics.Texture / PixelConverter.cs
PixelConverter.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.Common.Memory;
  3  using System;
  4  using System.Runtime.InteropServices;
  5  using System.Runtime.Intrinsics;
  6  using System.Runtime.Intrinsics.X86;
  7  
  8  namespace Ryujinx.Graphics.Texture
  9  {
 10      public static class PixelConverter
 11      {
 12          private static (int remainder, int outRemainder, int height) GetLineRemainders(int length, int width, int bpp, int outBpp)
 13          {
 14              int stride = BitUtils.AlignUp(width * bpp, LayoutConverter.HostStrideAlignment);
 15              int remainder = stride / bpp - width;
 16  
 17              int outStride = BitUtils.AlignUp(width * outBpp, LayoutConverter.HostStrideAlignment);
 18              int outRemainder = outStride / outBpp - width;
 19  
 20              return (remainder, outRemainder, length / stride);
 21          }
 22  
 23          public unsafe static MemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
 24          {
 25              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
 26              Span<byte> outputSpan = output.Span;
 27  
 28              (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
 29  
 30              Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
 31  
 32              if (remainder == 0)
 33              {
 34                  int start = 0;
 35  
 36                  if (Sse41.IsSupported)
 37                  {
 38                      int sizeTrunc = data.Length & ~7;
 39                      start = sizeTrunc;
 40  
 41                      fixed (byte* inputPtr = data, outputPtr = outputSpan)
 42                      {
 43                          for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
 44                          {
 45                              Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
 46                          }
 47                      }
 48                  }
 49  
 50                  for (int i = start; i < data.Length; i++)
 51                  {
 52                      outputSpanUInt16[i] = data[i];
 53                  }
 54              }
 55              else
 56              {
 57                  int offset = 0;
 58                  int outOffset = 0;
 59  
 60                  for (int y = 0; y < height; y++)
 61                  {
 62                      for (int x = 0; x < width; x++)
 63                      {
 64                          outputSpanUInt16[outOffset++] = data[offset++];
 65                      }
 66  
 67                      offset += remainder;
 68                      outOffset += outRemainder;
 69                  }
 70              }
 71  
 72              return output;
 73          }
 74  
 75          public static MemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
 76          {
 77              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
 78              int offset = 0;
 79              int outOffset = 0;
 80  
 81              (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 82  
 83              ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
 84              Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
 85  
 86              for (int y = 0; y < height; y++)
 87              {
 88                  for (int x = 0; x < width; x++)
 89                  {
 90                      uint packed = inputSpan[offset++];
 91  
 92                      uint outputPacked = 0xff000000;
 93                      outputPacked |= (packed << 3) & 0x000000f8;
 94                      outputPacked |= (packed << 8) & 0x00f80000;
 95  
 96                      // Replicate 5 bit components.
 97                      outputPacked |= (outputPacked >> 5) & 0x00070007;
 98  
 99                      // Include and replicate 6 bit component.
100                      outputPacked |= ((packed << 5) & 0x0000fc00) | ((packed >> 1) & 0x00000300);
101  
102                      outputSpan[outOffset++] = outputPacked;
103                  }
104  
105                  offset += remainder;
106                  outOffset += outRemainder;
107              }
108  
109              return output;
110          }
111  
112          public static MemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
113          {
114              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
115              int offset = 0;
116              int outOffset = 0;
117  
118              (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
119  
120              ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
121              Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
122  
123              for (int y = 0; y < height; y++)
124              {
125                  for (int x = 0; x < width; x++)
126                  {
127                      uint packed = inputSpan[offset++];
128  
129                      uint a = forceAlpha ? 1 : (packed >> 15);
130  
131                      uint outputPacked = a * 0xff000000;
132                      outputPacked |= (packed << 3) & 0x000000f8;
133                      outputPacked |= (packed << 6) & 0x0000f800;
134                      outputPacked |= (packed << 9) & 0x00f80000;
135  
136                      // Replicate 5 bit components.
137                      outputPacked |= (outputPacked >> 5) & 0x00070707;
138  
139                      outputSpan[outOffset++] = outputPacked;
140                  }
141  
142                  offset += remainder;
143                  outOffset += outRemainder;
144              }
145  
146              return output;
147          }
148  
149          public static MemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
150          {
151              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
152              int offset = 0;
153              int outOffset = 0;
154  
155              (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
156  
157              ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
158              Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
159  
160              for (int y = 0; y < height; y++)
161              {
162                  for (int x = 0; x < width; x++)
163                  {
164                      uint packed = inputSpan[offset++];
165  
166                      uint a = packed >> 15;
167  
168                      uint outputPacked = a * 0xff000000;
169                      outputPacked |= (packed >> 8) & 0x000000f8;
170                      outputPacked |= (packed << 5) & 0x0000f800;
171                      outputPacked |= (packed << 18) & 0x00f80000;
172  
173                      // Replicate 5 bit components.
174                      outputPacked |= (outputPacked >> 5) & 0x00070707;
175  
176                      outputSpan[outOffset++] = outputPacked;
177                  }
178  
179                  offset += remainder;
180                  outOffset += outRemainder;
181              }
182  
183              return output;
184          }
185  
186          public static MemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
187          {
188              MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
189              int offset = 0;
190              int outOffset = 0;
191  
192              (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
193  
194              ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
195              Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
196  
197              for (int y = 0; y < height; y++)
198              {
199                  for (int x = 0; x < width; x++)
200                  {
201                      uint packed = inputSpan[offset++];
202  
203                      uint outputPacked = packed & 0x0000000f;
204                      outputPacked |= (packed << 4) & 0x00000f00;
205                      outputPacked |= (packed << 8) & 0x000f0000;
206                      outputPacked |= (packed << 12) & 0x0f000000;
207  
208                      outputSpan[outOffset++] = outputPacked * 0x11;
209                  }
210  
211                  offset += remainder;
212                  outOffset += outRemainder;
213              }
214  
215              return output;
216          }
217      }
218  }