示例#1
0
    public byte[] LoadImage(string imagePath, Arranger arranger, ColorMatchStrategy matchStrategy)
    {
        using var inputImage = SixLabors.ImageSharp.Image.Load <Rgba32>(imagePath);
        var width  = inputImage.Width;
        var height = inputImage.Height;

        var outputImage = new byte[width * height];
        int destidx     = 0;

        for (int y = 0; y < height; y++)
        {
            var span = inputImage.GetPixelRowSpan(y);
            for (int x = 0; x < width; x++, destidx++)
            {
                if (arranger.GetElementAtPixel(x, y) is ArrangerElement el)
                {
                    var pal      = el.Palette;
                    var color    = new ColorRgba32(span[x].PackedValue);
                    var palIndex = pal.GetIndexByNativeColor(color, matchStrategy);
                    outputImage[destidx] = palIndex;
                }
            }
        }

        return(outputImage);
    }
示例#2
0
    /// <summary>
    /// Fills the surrounding, contiguous color area with a new color
    /// </summary>
    /// <param name="x">x-coordinate to start at in pixel coordinates</param>
    /// <param name="y">y-coordinate to start at in pixel coordinates</param>
    /// <param name="fillIndex">Palette index to fill with</param>
    /// <returns>True if any pixels were modified</returns>
    public static bool FloodFill(this DirectImage image, int x, int y, ColorRgba32 fillColor)
    {
        bool isModified   = false;
        var  replaceColor = image.GetPixel(x, y);

        if (fillColor.Color == replaceColor.Color)
        {
            return(false);
        }

        var openNodes = new Stack <(int x, int y)>();

        openNodes.Push((x, y));

        while (openNodes.Count > 0)
        {
            var nodePosition = openNodes.Pop();

            if (nodePosition.x >= 0 && nodePosition.x < image.Width && nodePosition.y >= 0 && nodePosition.y < image.Height)
            {
                var nodeColor = image.GetPixel(nodePosition.x, nodePosition.y);
                if (nodeColor.Color == replaceColor.Color)
                {
                    isModified = true;
                    image.SetPixel(nodePosition.x, nodePosition.y, fillColor);
                    openNodes.Push((nodePosition.x - 1, nodePosition.y));
                    openNodes.Push((nodePosition.x + 1, nodePosition.y));
                    openNodes.Push((nodePosition.x, nodePosition.y - 1));
                    openNodes.Push((nodePosition.x, nodePosition.y + 1));
                }
            }
        }

        return(isModified);
    }
示例#3
0
    public ColorBgr6 ToForeignColor(ColorRgba32 nc)
    {
        byte r = (byte)(nc.r / 85);
        byte b = (byte)(nc.b / 85);
        byte g = (byte)(nc.g / 85);

        return(new ColorBgr6(r, g, b));
    }
示例#4
0
    public ColorBgr15 ToForeignColor(ColorRgba32 nc)
    {
        byte r = (byte)(nc.r >> 3);
        byte g = (byte)(nc.g >> 3);
        byte b = (byte)(nc.b >> 3);

        return(new ColorBgr15(r, g, b));
    }
示例#5
0
 public ColorNes ToForeignColor(ColorRgba32 nc)
 {
     if (_nesPalette.TryGetIndexByNativeColor(nc, ColorMatchStrategy.Nearest, out var index))
     {
         return((ColorNes)_nesPalette.GetForeignColor(index));
     }
     throw new ArgumentException($"{nameof(ToForeignColor)} parameter (R: {nc.R}, G: {nc.G}, B: {nc.B}, A: {nc.A}) could not be matched in palette '{_nesPalette.Name}'");
 }
示例#6
0
    public ColorBgr9 ToForeignColor(ColorRgba32 nc)
    {
        byte r = (byte)MathF.Round(nc.r / (255f / 7f));
        byte g = (byte)MathF.Round(nc.g / (255f / 7f));
        byte b = (byte)MathF.Round(nc.b / (255f / 7f));

        return(new ColorBgr9(r, g, b));
    }
示例#7
0
    public ColorAbgr16 ToForeignColor(ColorRgba32 nc)
    {
        byte r = (byte)(nc.r >> 3);
        byte g = (byte)(nc.g >> 3);
        byte b = (byte)(nc.b >> 3);
        byte a = (byte)(nc.a <= AlphaSemiTransparent ? 0 : 1);

        return(new ColorAbgr16(r, g, b, a));
    }
示例#8
0
    public N64Rgba32Codec()
    {
        Width  = DefaultWidth;
        Height = DefaultHeight;

        _foreignBuffer = new byte[(StorageSize + 7) / 8];
        _nativeBuffer  = new ColorRgba32[Height, Width];

        _bitReader = BitStream.OpenRead(_foreignBuffer, StorageSize);
    }
示例#9
0
    public Rgb24TiledCodec(int width, int height)
    {
        Width  = width;
        Height = height;

        _foreignBuffer = new byte[(StorageSize + 7) / 8];
        _nativeBuffer  = new ColorRgba32[Height, Width];

        _bitReader = BitStream.OpenRead(_foreignBuffer, StorageSize);
    }
示例#10
0
    /// <summary>
    /// Tries to set a pixel's palette index at the specified pixel coordinate
    /// </summary>
    /// <param name="x">x-coordinate in pixel coordinates</param>
    /// <param name="y">y-coordinate in pixel coordinates</param>
    /// <param name="color">Palette index of color</param>
    /// <returns>True if set, false if not set</returns>
    public static MagitekResult TrySetPixel(this IndexedImage image, int x, int y, ColorRgba32 color)
    {
        var result = image.CanSetPixel(x, y, color);

        if (result.Value is MagitekResult.Success)
        {
            image.SetPixel(x, y, color);
        }

        return(result);
    }
示例#11
0
    /// <summary>
    /// Sets a pixel's palette index at the specified pixel coordinate
    /// </summary>
    /// <param name="x">x-coordinate in pixel coordinates</param>
    /// <param name="y">y-coordinate in pixel coordinates</param>
    /// <param name="color">Palette index of color</param>
    public static void SetPixel(this IndexedImage image, int x, int y, ColorRgba32 color)
    {
        var el = image.Arranger.GetElementAtPixel(x + image.Left, y + image.Top);

        if (el?.Palette is Palette pal)
        {
            var index = pal.GetIndexByNativeColor(color, ColorMatchStrategy.Exact);
            image.Image[x + image.Width * y] = index;
        }
        else
        {
            throw new InvalidOperationException($"{nameof(SetPixel)} cannot set pixel at ({x}, {y}) because there is no associated palette");
        }
    }
示例#12
0
    public void ToNative_AsExpected(IColor32 fc, ColorRgba32 expected)
    {
        var colorFactory = new ColorFactory();
        var actual       = colorFactory.ToNative(fc);

        Assert.Multiple(() =>
        {
            Assert.AreEqual(expected.Color, actual.Color, ".Color components not equal");
            Assert.AreEqual(expected.R, actual.R, "Red components not equal");
            Assert.AreEqual(expected.G, actual.G, "Green components not equal");
            Assert.AreEqual(expected.B, actual.B, "Blue components not equal");
            Assert.AreEqual(expected.A, actual.A, "Alpha components not equal");
        });
    }
示例#13
0
        public static void WritePAA(BinaryWriterEx writer, ColorRgba32[,] image, PAAType type = PAAType.UNDEFINED, PAAFlags flags = PAAFlags.InterpolatedAlpha)
        {
            var   max         = new ColorRgba32(0, 0, 0, 0);
            ulong sumR        = 0;
            ulong sumG        = 0;
            ulong sumB        = 0;
            ulong sumA        = 0;
            byte  minA        = 255;
            ulong totalPixels = 0;

            for (int y = 0; y < image.GetLength(0); ++y)
            {
                for (int x = 0; x < image.GetLength(1); ++x)
                {
                    var color = image[y, x];
                    max.r = Math.Max(color.r, max.r);
                    max.g = Math.Max(color.g, max.g);
                    max.b = Math.Max(color.b, max.b);
                    max.a = Math.Max(color.a, max.a);
                    minA  = Math.Min(color.a, minA);
                    if (color.a > 128)
                    {
                        sumR += color.r;
                        sumG += color.g;
                        sumB += color.b;
                        sumA += color.a;
                        totalPixels++;
                    }
                }
            }
            var avg = new ColorRgba32((byte)(sumR / totalPixels), (byte)(sumG / totalPixels), (byte)(sumB / totalPixels), (byte)(sumA / totalPixels));

            if (type == PAAType.UNDEFINED)
            {
                if (minA == 255)
                {
                    type = PAAType.DXT1;
                }
                else
                {
                    type = PAAType.DXT5;
                }
            }

            WritePAA(writer, new ReadOnlyMemory2D <ColorRgba32>(image), max, avg, type, flags);
        }
示例#14
0
    public void ToForeignColor_Converts_Correctly(ColorRgba32 nc, ColorBgr15 expected, ColorModel colorModel)
    {
        var colorFactory = new ColorFactory();
        var actual       = colorFactory.ToForeign(nc, colorModel);

        if (actual is IColor32 actual32)
        {
            Assert.Multiple(() =>
            {
                Assert.AreEqual(expected.Color, actual.Color, ".Color components not equal");
                Assert.AreEqual(expected.R, actual32.R, "Red components not equal");
                Assert.AreEqual(expected.G, actual32.G, "Green components not equal");
                Assert.AreEqual(expected.B, actual32.B, "Blue components not equal");
                Assert.AreEqual(expected.A, actual32.A, "Alpha components not equal");
            });
        }
    }
示例#15
0
 private static void Convert(string source, string target)
 {
     Console.WriteLine($"{source} -> {target}");
     using (var paaStream = File.OpenRead(source))
     {
         using (var img = Image.Load <Rgba32>(source))
         {
             var targetPixels = new ColorRgba32[img.Height, img.Width];
             for (int y = 0; y < img.Height; ++y)
             {
                 for (int x = 0; x < img.Width; ++x)
                 {
                     var srcPixel = img[x, y];
                     targetPixels[y, x] = new ColorRgba32(srcPixel.R, srcPixel.G, srcPixel.B, srcPixel.A);
                 }
             }
             PaaEncoder.WritePAA(target, targetPixels);
         }
     }
 }
示例#16
0
    /// <summary>
    /// Determines if a color can be set to an existing pixel's palette index at the specified pixel coordinate
    /// </summary>
    /// <param name="x">x-coordinate in pixel coordinates</param>
    /// <param name="y">y-coordinate in pixel coordinates</param>
    /// <param name="color">Palette index of color</param>
    public static MagitekResult CanSetPixel(this IndexedImage image, int x, int y, ColorRgba32 color)
    {
        if (x >= image.Width || y >= image.Height || x < 0 || y < 0)
        {
            return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because because it is outside of the Arranger"));
        }

        var el = image.Arranger.GetElementAtPixel(x + image.Left, y + image.Top);

        if (el is ArrangerElement element)
        {
            if (!(element.Codec is IIndexedCodec))
            {
                return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because the element's codec is not an indexed color type"));
            }

            var pal = element.Palette;

            if (pal is null)
            {
                return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because arranger '{image.Arranger.Name}' has no palette specified for the element"));
            }

            if (!pal.ContainsNativeColor(color))
            {
                return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because the palette '{pal.Name}' does not contain the native color ({color.R}, {color.G}, {color.B}, {color.A})"));
            }

            var index = pal.GetIndexByNativeColor(color, ColorMatchStrategy.Exact);
            if (index >= (1 << element.Codec.ColorDepth))
            {
                return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because the color is contained at an index outside of the codec's range"));
            }

            return(MagitekResult.SuccessResult);
        }
        else
        {
            return(new MagitekResult.Failed($"Cannot set pixel at ({x}, {y}) because the element is undefined"));
        }
    }
示例#17
0
    public MagitekResult TryLoadImage(string imagePath, Arranger arranger, ColorMatchStrategy matchStrategy, out byte[] image)
    {
        using var inputImage = SixLabors.ImageSharp.Image.Load <Rgba32>(imagePath);
        var width  = inputImage.Width;
        var height = inputImage.Height;

        if (width != arranger.ArrangerPixelSize.Width || height != arranger.ArrangerPixelSize.Height)
        {
            image = default;
            return(new MagitekResult.Failed($"Arranger dimensions ({arranger.ArrangerPixelSize.Width}, {arranger.ArrangerPixelSize.Height})" +
                                            $" do not match image dimensions ({width}, {height})"));
        }

        image = new byte[width * height];
        int destidx = 0;

        for (int y = 0; y < height; y++)
        {
            var span = inputImage.GetPixelRowSpan(y);
            for (int x = 0; x < width; x++, destidx++)
            {
                if (arranger.GetElementAtPixel(x, y) is ArrangerElement el)
                {
                    var pal   = el.Palette;
                    var color = new ColorRgba32(span[x].PackedValue);

                    if (pal.TryGetIndexByNativeColor(color, matchStrategy, out var palIndex))
                    {
                        image[destidx] = palIndex;
                    }
                    else
                    {
                        return(new MagitekResult.Failed($"Could not match image color (R: {color.R}, G: {color.G}, B: {color.B}, A: {color.A}) within palette '{pal.Name}'"));
                    }
                }
            }
        }

        return(MagitekResult.SuccessResult);
    }
示例#18
0
    public ColorRgba32[] LoadImage(string imagePath)
    {
        using var inputImage = SixLabors.ImageSharp.Image.Load <Rgba32>(imagePath);
        var width  = inputImage.Width;
        var height = inputImage.Height;

        var outputImage = new ColorRgba32[width * height];
        int destidx     = 0;

        for (int y = 0; y < height; y++)
        {
            var span = inputImage.GetPixelRowSpan(y);
            for (int x = 0; x < width; x++)
            {
                var color = new ColorRgba32(span[x].PackedValue);
                outputImage[destidx] = color;
                destidx++;
            }
        }

        return(outputImage);
    }
示例#19
0
        protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback)
        {
            input.Flatten(scratchSurface);

            var pixels = new ColorRgba32[scratchSurface.Width, scratchSurface.Height];

            unsafe
            {
                for (int y = 0; y < scratchSurface.Height; ++y)
                {
                    var src = scratchSurface.GetRowPointer(y);
                    for (int x = 0; x < scratchSurface.Width; ++x)
                    {
                        pixels[y, x] = new ColorRgba32((*src).R, (*src).G, (*src).B, (*src).A);
                        src++;
                    }
                }
            }

            using (var writer = new BinaryWriterEx(output, true))
            {
                PaaEncoder.WritePAA(writer, pixels);
            }
        }
示例#20
0
 public ProjectNativeColorSource(ColorRgba32 value)
 {
     Value = value;
 }
示例#21
0
        public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation)
        {
            var type = Bc7BlockType.Type5;

            Span <Bc7Block> outputs = stackalloc Bc7Block[4];

            for (int rotation = 0; rotation < 4; rotation++)
            {
                var      rotatedBlock = Bc7EncodingHelpers.RotateBlockColors(block, rotation);
                Bc7Block output       = new Bc7Block();

                Bc7EncodingHelpers.GetInitialUnscaledEndpoints(rotatedBlock, out var ep0, out var ep1);

                ColorRgba32 scaledEp0 =
                    Bc7EncodingHelpers.ScaleDownEndpoint(ep0, type, false, out byte _);
                ColorRgba32 scaledEp1 =
                    Bc7EncodingHelpers.ScaleDownEndpoint(ep1, type, false, out byte _);

                byte pBit = 0;                 //fake pBit

                Bc7EncodingHelpers.OptimizeSubsetEndpointsWithPBit(type, rotatedBlock, ref scaledEp0,
                                                                   ref scaledEp1, ref pBit, ref pBit, startingVariation, partitionTable, subset, false, true);

                ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0);
                ep1 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp1, 0);
                byte[] colorIndices = new byte[16];
                byte[] alphaIndices = new byte[16];
                Bc7EncodingHelpers.FillAlphaColorIndices(type, rotatedBlock,
                                                         ep0,
                                                         ep1,
                                                         colorIndices, alphaIndices);

                bool needsRedo = false;

                if ((colorIndices[0] & 0b10) > 0)                 //If anchor index most significant bit is 1, switch endpoints
                {
                    var c      = scaledEp0;
                    var alpha0 = scaledEp0.a;
                    var alpha1 = scaledEp1.a;

                    scaledEp0   = scaledEp1;
                    scaledEp1   = c;
                    scaledEp0.a = alpha0;
                    scaledEp1.a = alpha1;

                    needsRedo = true;
                }
                if ((alphaIndices[0] & 0b10) > 0)                 //If anchor index most significant bit is 1, switch endpoints
                {
                    var a = scaledEp0.a;

                    scaledEp0.a = scaledEp1.a;
                    scaledEp1.a = a;

                    needsRedo = true;
                }

                if (needsRedo)
                {
                    //redo indices
                    ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0);
                    ep1 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp1, 0);
                    Bc7EncodingHelpers.FillAlphaColorIndices(type, rotatedBlock,
                                                             ep0,
                                                             ep1,
                                                             colorIndices, alphaIndices);
                }


                output.PackType5(rotation, new[] {
                    new byte[] { scaledEp0.r, scaledEp0.g, scaledEp0.b },
                    new byte[] { scaledEp1.r, scaledEp1.g, scaledEp1.b },
                },
                                 new[] { scaledEp0.a, scaledEp1.a },
                                 colorIndices, alphaIndices);

                outputs[rotation] = output;
            }

            int   bestIndex = 0;
            float bestError = 0;
            bool  first     = true;

            // Find best out of generated blocks
            for (int i = 0; i < outputs.Length; i++)
            {
                var decoded = outputs[i].Decode();

                float error = block.CalculateYCbCrAlphaError(decoded);
                if (error < bestError || first)
                {
                    first     = false;
                    bestError = error;
                    bestIndex = i;
                }
            }

            return(outputs[bestIndex]);
        }
示例#22
0
        public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation, int bestPartition)
        {
            Bc7Block           output = new Bc7Block();
            const Bc7BlockType type   = Bc7BlockType.Type2;

            ColorRgba32[]      endpoints      = new ColorRgba32[6];
            ReadOnlySpan <int> partitionTable = Bc7Block.Subsets3PartitionTable[bestPartition];

            byte[] indices = new byte[16];

            int[] anchorIndices = new int[] {
                0,
                Bc7Block.Subsets3AnchorIndices2[bestPartition],
                Bc7Block.Subsets3AnchorIndices3[bestPartition]
            };

            for (int subset = 0; subset < 3; subset++)
            {
                Bc7EncodingHelpers.GetInitialUnscaledEndpointsForSubset(block, out var ep0, out var ep1,
                                                                        partitionTable, subset);
                ColorRgba32 scaledEp0 =
                    Bc7EncodingHelpers.ScaleDownEndpoint(ep0, type, true, out byte _);
                ColorRgba32 scaledEp1 =
                    Bc7EncodingHelpers.ScaleDownEndpoint(ep1, type, true, out byte _);

                byte pBit = 0;
                Bc7EncodingHelpers.OptimizeSubsetEndpointsWithPBit(type, block, ref scaledEp0,
                                                                   ref scaledEp1, ref pBit, ref pBit, startingVariation, partitionTable, subset, false, false);

                ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0);
                ep1 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp1, 0);
                Bc7EncodingHelpers.FillSubsetIndices(type, block,
                                                     ep0,
                                                     ep1,
                                                     partitionTable, subset, indices);

                if ((indices[anchorIndices[subset]] & 0b10) > 0)                 //If anchor index most significant bit is 1, switch endpoints
                {
                    var c = scaledEp0;

                    scaledEp0 = scaledEp1;
                    scaledEp1 = c;

                    //redo indices
                    ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0);
                    ep1 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp1, 0);
                    Bc7EncodingHelpers.FillSubsetIndices(type, block,
                                                         ep0,
                                                         ep1,
                                                         partitionTable, subset, indices);
                }

                endpoints[subset * 2]     = scaledEp0;
                endpoints[subset * 2 + 1] = scaledEp1;
            }

            output.PackType2(bestPartition, new[] {
                new byte[] { endpoints[0].r, endpoints[0].g, endpoints[0].b },
                new byte[] { endpoints[1].r, endpoints[1].g, endpoints[1].b },
                new byte[] { endpoints[2].r, endpoints[2].g, endpoints[2].b },
                new byte[] { endpoints[3].r, endpoints[3].g, endpoints[3].b },
                new byte[] { endpoints[4].r, endpoints[4].g, endpoints[4].b },
                new byte[] { endpoints[5].r, endpoints[5].g, endpoints[5].b }
            },
                             indices);

            return(output);
        }
示例#23
0
        public static void WritePAA(BinaryWriterEx writer, ReadOnlyMemory2D <ColorRgba32> image, ColorRgba32 max, ColorRgba32 avg, PAAType type = PAAType.DXT5, PAAFlags flags = PAAFlags.InterpolatedAlpha)
        {
            if (type != PAAType.DXT5 && type != PAAType.DXT1)
            {
                throw new NotSupportedException();
            }

            var enc = new BCnEncoder.Encoder.BcEncoder(type == PAAType.DXT5 ? CompressionFormat.Bc3 : CompressionFormat.Bc1);

            var mipmaps = new List <MipmapEncoder>();

            var width  = image.Width;
            var height = image.Width;
            var offset = type == PAAType.DXT5 ? 128 : 112;

            foreach (var mipmap in enc.EncodeToRawBytes(image))
            {
                if (width > 2 || height > 2)
                {
                    var menc = new MipmapEncoder(mipmap, width, height, offset);
                    offset += menc.MipmapEntrySize;
                    mipmaps.Add(menc);
                    width  = width / 2;
                    height = height / 2;
                }
            }

            writer.Write(type == PAAType.DXT5 ? (ushort)0xff05 : (ushort)0xff01);

            writer.WriteAscii("GGATCGVA", 8);
            writer.Write((uint)4);
            writer.Write(avg.r);
            writer.Write(avg.g);
            writer.Write(avg.b);
            writer.Write(avg.a);

            writer.WriteAscii("GGATCXAM", 8);
            writer.Write((uint)4);
            writer.Write(max.r);
            writer.Write(max.g);
            writer.Write(max.b);
            writer.Write(max.a);

            if (type == PAAType.DXT5)
            {
                writer.WriteAscii("GGATGALF", 8);
                writer.Write((uint)4);
                writer.Write((uint)flags);
            }

            writer.WriteAscii("GGATSFFO", 8);
            writer.Write(16 * 4);
            for (int i = 0; i < 16; ++i)
            {
                if (i < mipmaps.Count)
                {
                    writer.Write((uint)mipmaps[i].Offset);
                }
                else
                {
                    writer.Write((uint)0);
                }
            }

            writer.Write((ushort)0x0000);

            int index = 0;

            foreach (var mipmap in mipmaps)
            {
                if (writer.Position != mipmap.Offset)
                {
                    throw new Exception($"Wrong offset @{mipmap.Width} : {writer.Position} != { mipmap.Offset}");
                }
                writer.Write(mipmap.WidthEncoded);
                writer.Write(mipmap.Height);
                writer.WriteUInt24((uint)mipmap.PaaData.Length);
                writer.Write(mipmap.PaaData);
                index++;
                width  = width / 2;
                height = height / 2;
            }
            writer.Write((uint)0x0000);
            writer.Write((ushort)0x0000);
        }
示例#24
0
		public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation)
		{
			bool hasAlpha = block.HasTransparentPixels();

			Bc7Block output = new Bc7Block();
			Bc7EncodingHelpers.GetInitialUnscaledEndpoints(block, out var ep0, out var ep1);

			ColorRgba32 scaledEp0 =
				Bc7EncodingHelpers.ScaleDownEndpoint(ep0, Bc7BlockType.Type6, false, out byte pBit0);
			ColorRgba32 scaledEp1 =
				Bc7EncodingHelpers.ScaleDownEndpoint(ep1, Bc7BlockType.Type6, false, out byte pBit1);

			ReadOnlySpan<int> partitionTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
			const int subset = 0;

			//Force 255 alpha if fully opaque
			if (!hasAlpha)
			{
				pBit0 = 1;
				pBit1 = 1;
			}

			Bc7EncodingHelpers.OptimizeSubsetEndpointsWithPBit(Bc7BlockType.Type6, block, ref scaledEp0,
				ref scaledEp1, ref pBit0, ref pBit1, startingVariation, partitionTable, subset, hasAlpha, hasAlpha);

			ep0 = Bc7EncodingHelpers.ExpandEndpoint(Bc7BlockType.Type6, scaledEp0, pBit0);
			ep1 = Bc7EncodingHelpers.ExpandEndpoint(Bc7BlockType.Type6, scaledEp1, pBit1);
			byte[] indices = new byte[16];
			Bc7EncodingHelpers.FillSubsetIndices(Bc7BlockType.Type6, block,
				ep0,
				ep1,
				partitionTable, subset, indices);

			

			if ((indices[0] & 0b1000) > 0) //If anchor index most significant bit is 1, switch endpoints
			{
				var c = scaledEp0;
				var p = pBit0;

				scaledEp0 = scaledEp1;
				pBit0 = pBit1;
				scaledEp1 = c;
				pBit1 = p;

				//redo indices
				ep0 = Bc7EncodingHelpers.ExpandEndpoint(Bc7BlockType.Type6, scaledEp0, pBit0);
				ep1 = Bc7EncodingHelpers.ExpandEndpoint(Bc7BlockType.Type6, scaledEp1, pBit1);
				Bc7EncodingHelpers.FillSubsetIndices(Bc7BlockType.Type6, block,
					ep0,
					ep1,
					partitionTable, subset, indices);
			}

			output.PackType6(new[]{
					new byte[]{scaledEp0.r, scaledEp0.g, scaledEp0.b, scaledEp0.a},
					new byte[]{scaledEp1.r, scaledEp1.g, scaledEp1.b, scaledEp1.a},
				},
				new[] { pBit0, pBit1 },
				indices);

			return output;
		}