public static void Main(string[] args) { if (args.Length < 4) { Console.WriteLine("Format: {path} {width} {height} {blockXSize} {blockYSize}"); } else { string path = args[0]; int width = int.Parse(args[1]); int height = int.Parse(args[2]); int blockXSize = int.Parse(args[3]); int blockYSize = int.Parse(args[4]); byte[] data = File.ReadAllBytes(path); using (DirectBitmap bitmap = new DirectBitmap(width, height)) { AstcDecoder decoder = new AstcDecoder(); decoder.DecodeASTC(data, width, height, blockXSize, blockYSize, bitmap.Bits); string dirPath = Path.GetDirectoryName(path); string name = Path.GetFileNameWithoutExtension(path); string newPath = Path.Combine(dirPath, name + "_decoded.png"); bitmap.Bitmap.Save(newPath, ImageFormat.Png); } Console.WriteLine("Finished"); } Console.ReadKey(); }
public static Bitmap GetBitmap(Texture texture, int ArrayLevel = 0, int MipLevel = 0, int DepthLevel = 0) { int width = (int)Math.Max(1, texture.Width >> MipLevel); int height = (int)Math.Max(1, texture.Height >> MipLevel); TextureFormatInfo formatInfo = TextureFormatInfo.FormatTable[texture.Format]; Memory <byte> data = GetImageData(texture, ArrayLevel, MipLevel, DepthLevel); if (AstcDecoder.CanHandle(texture)) { if (AstcDecoder.TryDecodeToRgba8(data, (int)formatInfo.BlockWidth, (int)formatInfo.BlockHeight, width, height, 1, 1, out var decoded)) { return(GetBitmapFromBytes(ConvertBgraToRgba(decoded), width, height, PixelFormat.Format32bppArgb)); } } else if (DDSDecoder.CanHandle(texture)) { return(DDSDecoder.Decompress(data.Span, width, height, texture.Format)); } else if (RgbaDecoder.CanHandle(texture)) { return(RgbaDecoder.Decode(data.Span, width, height, texture.Format)); } return(null); }
/// <summary> /// Converts texture data to a format and layout that is supported by the host GPU. /// </summary> /// <param name="data">Data to be converted</param> /// <returns>Converted data</returns> private ReadOnlySpan <byte> ConvertToHostCompatibleFormat(ReadOnlySpan <byte> data) { if (Info.IsLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( Info.Width, Info.Height, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Stride, Info.FormatInfo.BytesPerPixel, data); } else { data = LayoutConverter.ConvertBlockLinearToLinear( Info.Width, Info.Height, _depth, Info.Levels, _layers, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.FormatInfo.BytesPerPixel, Info.GobBlocksInY, Info.GobBlocksInZ, Info.GobBlocksInTileX, _sizeInfo, data); } if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc()) { if (!AstcDecoder.TryDecodeToRgba8( data.ToArray(), Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Width, Info.Height, _depth, Info.Levels, _layers, out Span <byte> decoded)) { string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}"; Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.Address:X} ({texInfo})."); } data = decoded; } return(data); }
private static bool IsTextureDecodable(Texture texture) { if (AstcDecoder.CanHandle(texture)) { return(true); } if (DDSDecoder.CanHandle(texture)) { return(true); } if (RgbaDecoder.CanHandle(texture)) { return(true); } return(false); }
public static DirectBitmap ASTCTextureToBitmap(Texture2D texture, byte[] data) { int width = texture.Width; int height = texture.Height; int blockSize = texture.ASTCBlockSize(); DirectBitmap bitmap = new DirectBitmap(width, height); try { AstcDecoder.DecodeASTC(data, width, height, blockSize, blockSize, bitmap.Bits); return(bitmap); } catch { bitmap.Dispose(); throw; } }
/// <summary> /// Converts texture data to a format and layout that is supported by the host GPU. /// </summary> /// <param name="data">Data to be converted</param> /// <returns>Converted data</returns> private ReadOnlySpan <byte> ConvertToHostCompatibleFormat(ReadOnlySpan <byte> data) { if (Info.IsLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( Info.Width, Info.Height, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Stride, Info.FormatInfo.BytesPerPixel, data); } else { data = LayoutConverter.ConvertBlockLinearToLinear( Info.Width, Info.Height, _depth, Info.Levels, _layers, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.FormatInfo.BytesPerPixel, Info.GobBlocksInY, Info.GobBlocksInZ, Info.GobBlocksInTileX, _sizeInfo, data); } // Handle compressed cases not supported by the host: // - ASTC is usually not supported on desktop cards. // - BC4/BC5 is not supported on 3D textures. if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc()) { if (!AstcDecoder.TryDecodeToRgba8( data.ToArray(), Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Width, Info.Height, _depth, Info.Levels, _layers, out Span <byte> decoded)) { string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}"; Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.Address:X} ({texInfo})."); } data = decoded; } else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc4()) { data = BCnDecoder.DecodeBC4(data, Info.Width, Info.Height, _depth, Info.Levels, _layers, Info.FormatInfo.Format == Format.Bc4Snorm); } else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc5()) { data = BCnDecoder.DecodeBC5(data, Info.Width, Info.Height, _depth, Info.Levels, _layers, Info.FormatInfo.Format == Format.Bc5Snorm); } return(data); }
public void Create(long key, byte[] data, GalImage image) { int handle = GL.GenTexture(); TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); GL.BindTexture(target, handle); const int level = 0; //TODO: Support mipmap textures. const int border = 0; _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length); if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format)) { InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format); switch (target) { case TextureTarget.Texture1D: GL.CompressedTexImage1D( target, level, internalFmt, image.Width, border, data.Length, data); break; case TextureTarget.Texture2D: GL.CompressedTexImage2D( target, level, internalFmt, image.Width, image.Height, border, data.Length, data); break; case TextureTarget.Texture3D: GL.CompressedTexImage3D( target, level, internalFmt, image.Width, image.Height, image.Depth, border, data.Length, data); break; // Cube map arrays are just 2D texture arrays with 6 entries // per cube map so we can handle them in the same way case TextureTarget.TextureCubeMapArray: case TextureTarget.Texture2DArray: GL.CompressedTexImage3D( target, level, internalFmt, image.Width, image.Height, image.LayerCount, border, data.Length, data); break; case TextureTarget.TextureCubeMap: Span <byte> array = new Span <byte>(data); int faceSize = ImageUtils.GetSize(image) / 6; for (int Face = 0; Face < 6; Face++) { GL.CompressedTexImage2D( TextureTarget.TextureCubeMapPositiveX + Face, level, internalFmt, image.Width, image.Height, border, faceSize, array.Slice(Face * faceSize, faceSize).ToArray()); } break; default: throw new NotImplementedException($"Unsupported texture target type: {target}"); } } else { //TODO: Use KHR_texture_compression_astc_hdr when available if (IsAstc(image.Format)) { int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format); int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format); int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format); data = AstcDecoder.DecodeToRgba8888( data, textureBlockWidth, textureBlockHeight, textureBlockDepth, image.Width, image.Height, image.Depth); image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask); } (PixelInternalFormat internalFmt, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(image.Format); switch (target) { case TextureTarget.Texture1D: GL.TexImage1D( target, level, internalFmt, image.Width, border, format, type, data); break; case TextureTarget.Texture2D: GL.TexImage2D( target, level, internalFmt, image.Width, image.Height, border, format, type, data); break; case TextureTarget.Texture3D: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.Depth, border, format, type, data); break; // Cube map arrays are just 2D texture arrays with 6 entries // per cube map so we can handle them in the same way case TextureTarget.TextureCubeMapArray: case TextureTarget.Texture2DArray: GL.TexImage3D( target, level, internalFmt, image.Width, image.Height, image.LayerCount, border, format, type, data); break; case TextureTarget.TextureCubeMap: Span <byte> array = new Span <byte>(data); int faceSize = ImageUtils.GetSize(image) / 6; for (int face = 0; face < 6; face++) { GL.TexImage2D( TextureTarget.TextureCubeMapPositiveX + face, level, internalFmt, image.Width, image.Height, border, format, type, array.Slice(face * faceSize, faceSize).ToArray()); } break; default: throw new NotImplementedException($"Unsupported texture target type: {target}"); } } }
/// <summary> /// Synchronizes guest and host memory. /// This will overwrite the texture data with the texture data on the guest memory, if a CPU /// modification is detected. /// Be aware that this can cause texture data written by the GPU to be lost, this is just a /// one way copy (from CPU owned to GPU owned memory). /// </summary> public void SynchronizeMemory() { if (_sequenceNumber == _context.SequenceNumber && _hasData) { return; } _sequenceNumber = _context.SequenceNumber; bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.Texture).Length != 0; if (!modified && _hasData) { return; } ReadOnlySpan <byte> data = _context.PhysicalMemory.GetSpan(Address, Size); if (Info.IsLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( Info.Width, Info.Height, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Stride, Info.FormatInfo.BytesPerPixel, data); } else { data = LayoutConverter.ConvertBlockLinearToLinear( Info.Width, Info.Height, _depth, Info.Levels, _layers, Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.FormatInfo.BytesPerPixel, Info.GobBlocksInY, Info.GobBlocksInZ, Info.GobBlocksInTileX, _sizeInfo, data); } if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc()) { if (!AstcDecoder.TryDecodeToRgba8( data.ToArray(), Info.FormatInfo.BlockWidth, Info.FormatInfo.BlockHeight, Info.Width, Info.Height, _depth, Info.Levels, out Span <byte> decoded)) { string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}"; Logger.PrintDebug(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.Address:X} ({texInfo})."); } data = decoded; } HostTexture.SetData(data); _hasData = true; }