public static bool Convert2DToDDS(string virtualFileName, string outputRealFileName, DDSImage.FormatEnum outputFormat, bool normalMap, bool generateMipmaps, out Vector2I sourceFileSize, out PixelFormat sourceFileFormat, out string error)
        {
            sourceFileSize   = Vector2I.Zero;
            sourceFileFormat = PixelFormat.Unknown;

            if (!ImageUtility.LoadFromVirtualFile(virtualFileName, out var data, out var size, out var depth, out var format, out var numFaces, out var numMipmaps, out error))
            {
                return(false);
            }

            sourceFileSize   = size;
            sourceFileFormat = format;

            byte[] rgba = new byte[size.X * size.Y * 4];

            if (format != PixelFormat.R8G8B8A8)
            {
                for (int y = 0; y < size.Y; y++)
                {
                    for (int x = 0; x < size.X; x++)
                    {
                        byte r;
                        byte g;
                        byte b;
                        byte a = 255;
                        int  offset;

                        switch (format)
                        {
                        case PixelFormat.R8G8B8:
                            offset = (y * size.X + x) * 3;
                            r      = data[offset + 2];
                            g      = data[offset + 1];
                            b      = data[offset + 0];
                            break;

                        case PixelFormat.X8R8G8B8:
                            offset = (y * size.X + x) * 4;
                            r      = data[offset + 2];
                            g      = data[offset + 1];
                            b      = data[offset + 0];
                            break;

                        case PixelFormat.A8R8G8B8:
                            offset = (y * size.X + x) * 4;
                            a      = data[offset + 3];
                            r      = data[offset + 2];
                            g      = data[offset + 1];
                            b      = data[offset + 0];
                            break;

                        case PixelFormat.L8:
                            offset = (y * size.X + x);
                            r      = g = b = data[offset];
                            break;

                        case PixelFormat.ShortRGB:
                            unsafe
                            {
                                fixed(byte *pData = data)
                                {
                                    ushort *pData2 = (ushort *)pData;

                                    offset = (y * size.X + x) * 3;
                                    r      = (byte)((float)pData2[offset + 0] / ushort.MaxValue * 255.0f);
                                    g      = (byte)((float)pData2[offset + 1] / ushort.MaxValue * 255.0f);
                                    b      = (byte)((float)pData2[offset + 2] / ushort.MaxValue * 255.0f);
                                }
                            }
                            break;

                        case PixelFormat.ShortRGBA:
                            unsafe
                            {
                                fixed(byte *pData = data)
                                {
                                    ushort *pData2 = (ushort *)pData;

                                    offset = (y * size.X + x) * 4;
                                    r      = (byte)((float)pData2[offset + 0] / ushort.MaxValue * 255.0f);
                                    g      = (byte)((float)pData2[offset + 1] / ushort.MaxValue * 255.0f);
                                    b      = (byte)((float)pData2[offset + 2] / ushort.MaxValue * 255.0f);
                                    a      = (byte)((float)pData2[offset + 3] / ushort.MaxValue * 255.0f);
                                }
                            }
                            break;

                        case PixelFormat.Float32RGB:
                            unsafe
                            {
                                fixed(byte *pData = data)
                                {
                                    float *pData2 = (float *)pData;

                                    offset = (y * size.X + x) * 3;
                                    r      = (byte)MathEx.Clamp((float)pData2[offset + 0] * 255.0f, 0.0f, 255.0f);
                                    g      = (byte)MathEx.Clamp((float)pData2[offset + 1] * 255.0f, 0.0f, 255.0f);
                                    b      = (byte)MathEx.Clamp((float)pData2[offset + 2] * 255.0f, 0.0f, 255.0f);
                                }
                            }
                            break;

                        case PixelFormat.Float32RGBA:
                            unsafe
                            {
                                fixed(byte *pData = data)
                                {
                                    float *pData2 = (float *)pData;

                                    offset = (y * size.X + x) * 4;
                                    r      = (byte)MathEx.Clamp((float)pData2[offset + 0] * 255.0f, 0.0f, 255.0f);
                                    g      = (byte)MathEx.Clamp((float)pData2[offset + 1] * 255.0f, 0.0f, 255.0f);
                                    b      = (byte)MathEx.Clamp((float)pData2[offset + 2] * 255.0f, 0.0f, 255.0f);
                                    a      = (byte)MathEx.Clamp((float)pData2[offset + 3] * 255.0f, 0.0f, 255.0f);
                                }
                            }
                            break;

                        default:
                            error = $"Conversion from \'{format}\' format is not supported.";
                            return(false);
                        }

                        //copy to rgba array
                        offset           = (y * size.X + x) * 4;
                        rgba[offset + 0] = r;
                        rgba[offset + 1] = g;
                        rgba[offset + 2] = b;
                        rgba[offset + 3] = a;
                    }
                }
            }

            var image = GenerateDDS(rgba, size, outputFormat, normalMap, generateMipmaps, out error);

            if (image == null)
            {
                return(false);
            }

            if (!WriteToFile(outputRealFileName, image, out error))
            {
                return(false);
            }

            error = "";
            return(true);

            //MapFormats mapFormat = textureData.normalMap ? NormalMapFormat : BaseMapFormat;

            //DDSImage.FormatTypes ddsFormat = DDSImage.FormatTypes.DXT1;
            //switch( mapFormat )
            //{
            //case MapFormats.DDS_DXT1:
            //	ddsFormat = DDSImage.FormatTypes.DXT1;
            //	break;
            //case MapFormats.DDS_DXT5:
            //	ddsFormat = DDSImage.FormatTypes.DXT5;
            //	break;
            //case MapFormats.DDS_3DC:
            //	ddsFormat = DDSImage.FormatTypes._3DC;
            //	break;
            //case MapFormats.DDS_ARGB:
            //	ddsFormat = DDSImage.FormatTypes.A8R8G8B8;
            //	break;
            //}

            //DDSImage ddsImage = DDSImageManager.Instance.Generate( rgba, size, ddsFormat, true );

            //bool oldExists = File.Exists( outputRealFullPath );
            //using( FileStream stream = new FileStream( outputRealFullPath, FileMode.Create ) )
            //{
            //	DDSImageManager.WriteFile( stream, ddsImage );
            //}
            //createdFiles.Add( new CreatedFileItem( outputRealFullPath, oldExists ) );

            //error = null;
            //return true;
        }
        public static DDSImage GenerateDDS(byte[] rgba, Vector2I size, DDSImage.FormatEnum format, bool normalMap, bool generateMipmaps, out string error)
        {
            //!!!!может не инициализировать каждый раз

            NvidiaTextureTools.Compressor         compressor         = null;
            NvidiaTextureTools.InputOptions       inputOptions       = null;
            NvidiaTextureTools.CompressionOptions compressionOptions = null;
            NvidiaTextureTools.OutputOptions      outputOptions      = null;

            try
            {
                NativeLibraryManager.PreLoadLibrary("nvtt");

                compressor         = new NvidiaTextureTools.Compressor();
                inputOptions       = new NvidiaTextureTools.InputOptions();
                compressionOptions = new NvidiaTextureTools.CompressionOptions();
                outputOptions      = new NvidiaTextureTools.OutputOptions();

                inputOptions.SetTextureLayout(NvidiaTextureTools.TextureType.Texture2D, size.X, size.Y, 1);

                byte[] bgra = new byte[rgba.Length];
                //bool containAlpha = false;
                {
                    for (int y = 0; y < size.Y; y++)
                    {
                        for (int x = 0; x < size.X; x++)
                        {
                            int offset = (y * size.X + x) * 4;
                            bgra[offset + 0] = rgba[offset + 2];
                            bgra[offset + 1] = rgba[offset + 1];
                            bgra[offset + 2] = rgba[offset + 0];
                            bgra[offset + 3] = rgba[offset + 3];

                            //byte alpha = bgra[ offset + 3 ];
                            //if( alpha != 255 )
                            //	containAlpha = true;
                        }
                    }
                }

                unsafe
                {
                    fixed(byte *pData = bgra)
                    inputOptions.SetMipmapData((IntPtr)pData, size.X, size.Y, 1, 0, 0);
                }

                inputOptions.SetWrapMode(NvidiaTextureTools.WrapMode.Repeat);
                inputOptions.SetFormat(NvidiaTextureTools.InputFormat.BGRA_8UB);
                if (normalMap)
                {
                    inputOptions.SetNormalMap(true);
                }
                inputOptions.SetMipmapGeneration(generateMipmaps);

                inputOptions.SetRoundMode(NvidiaTextureTools.RoundMode.ToNextPowerOfTwo);

                //sense?
                if (format == DDSImage.FormatEnum.DXT5)
                {
                    inputOptions.SetAlphaMode(NvidiaTextureTools.AlphaMode.Transparency);
                }
                //if( containAlpha )
                //{
                //	if( format == DDSImage.FormatEnum.DXT1 )
                //		inputOptions.SetAlphaMode( NvidiaTextureTools.AlphaMode.Premultiplied );
                //	else
                //		inputOptions.SetAlphaMode( NvidiaTextureTools.AlphaMode.Transparency );
                //}
                //else
                //	inputOptions.SetAlphaMode( NvidiaTextureTools.AlphaMode.None );

                //inputOptions.SetNormalMap( format == DDSImage.FormatTypes._3DC );
                //inputOptions.SetNormalMap( true );
                //inputOptions.SetConvertToNormalMap( true );
                //public void SetHeightEvaluation(float redScale, float greenScale, float blueScale, float alphaScale)
                //public void SetNormalFilter(float small, float medium, float big, float large)
                //inputOptions.SetNormalizeMipmaps( true );
                //public void SetColorTransform(ColorTransform t);
                //public void SetLinearTransfrom(int channel, float w0, float w1, float w2, float w3)

                switch (format)
                {
                case DDSImage.FormatEnum.DXT1:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.DXT1a);
                    //compressionOptions.SetFormat( containAlpha ? NvidiaTextureTools.Format.DXT1a : NvidiaTextureTools.Format.DXT1 );
                    break;

                case DDSImage.FormatEnum.DXT3:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.DXT3);
                    break;

                case DDSImage.FormatEnum.DXT5:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.DXT5);
                    break;

                case DDSImage.FormatEnum.BC5:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.BC5);
                    break;

                case DDSImage.FormatEnum.R8G8B8:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.RGB);
                    break;

                case DDSImage.FormatEnum.A8R8G8B8:
                    compressionOptions.SetFormat(NvidiaTextureTools.Format.RGBA);
                    break;

                default:
                    error = $"Format \'{format}\' is not supported.";
                    break;
                }

                //!!!!зависает BC5 на Highest и Production
                if (format == DDSImage.FormatEnum.BC5)
                {
                    compressionOptions.SetQuality(NvidiaTextureTools.Quality.Normal);
                }
                else
                {
                    compressionOptions.SetQuality(NvidiaTextureTools.Quality.Highest);
                }
                //compressionOptions.SetQuality( NvidiaTextureTools.Quality.Production );

                outputOptions.SetOutputHeader(false);
                outputOptions.SetOutputHandler(OutputOptions_BeginImage, OutputOptions_WriteData, OutputOptions_EndImage);

                generatingSurfaces = new List <DDSImage.Surface>();

                if (!compressor.Compress(inputOptions, compressionOptions, outputOptions))
                {
                    error = "Compression failed.";
                    return(null);
                }

                DDSImage.FormatEnum resultFormat = format;
                if (resultFormat == DDSImage.FormatEnum.R8G8B8)
                {
                    resultFormat = DDSImage.FormatEnum.A8R8G8B8;
                }
                DDSImage ddsImage = new DDSImage(resultFormat, generatingSurfaces.ToArray(), false);

                error = "";
                return(ddsImage);
            }
            catch (Exception e)
            {
                error = e.Message;
                return(null);
            }
            finally
            {
                generatingSurfaces          = null;
                generatingSurfaceDataOffset = 0;

                inputOptions?.Dispose();
                compressionOptions?.Dispose();
                outputOptions?.Dispose();
                compressor?.Dispose();
            }
        }