public SKBitmap GenerateBitmap() { Reader.BaseStream.Position = DataOffset; var width = ActualWidth >> MipmapLevelToExtract; var height = ActualHeight >> MipmapLevelToExtract; var blockWidth = Width >> MipmapLevelToExtract; var blockHeight = Height >> MipmapLevelToExtract; var skiaBitmap = new SKBitmap(width, height, SKColorType.Bgra8888, SKAlphaType.Unpremul); SkipMipmaps(); switch (Format) { case VTexFormat.DXT1: return(TextureDecompressors.UncompressDXT1(skiaBitmap, GetTextureSpan(), blockWidth, blockHeight)); case VTexFormat.DXT5: var yCoCg = false; var normalize = false; var invert = false; var hemiOct = false; if (Resource.EditInfo.Structs.ContainsKey(ResourceEditInfo.REDIStruct.SpecialDependencies)) { var specialDeps = (SpecialDependencies)Resource.EditInfo.Structs[ResourceEditInfo.REDIStruct.SpecialDependencies]; yCoCg = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version Image YCoCg Conversion"); normalize = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version Image NormalizeNormals"); invert = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version LegacySource1InvertNormals"); hemiOct = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version Mip HemiOctAnisoRoughness"); } return(TextureDecompressors.UncompressDXT5(skiaBitmap, GetTextureSpan(), blockWidth, blockHeight, yCoCg, normalize, invert, hemiOct)); case VTexFormat.I8: return(TextureDecompressors.ReadI8(skiaBitmap, GetTextureSpan())); case VTexFormat.RGBA8888: return(TextureDecompressors.ReadRGBA8888(skiaBitmap, GetTextureSpan())); case VTexFormat.R16: return(TextureDecompressors.ReadR16(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RG1616: return(TextureDecompressors.ReadRG1616(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RGBA16161616: return(TextureDecompressors.ReadRGBA16161616(skiaBitmap, GetTextureSpan())); case VTexFormat.R16F: return(TextureDecompressors.ReadR16F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RG1616F: return(TextureDecompressors.ReadRG1616F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RGBA16161616F: return(TextureDecompressors.ReadRGBA16161616F(skiaBitmap, GetTextureSpan())); case VTexFormat.R32F: return(TextureDecompressors.ReadR32F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RG3232F: return(TextureDecompressors.ReadRG3232F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RGB323232F: return(TextureDecompressors.ReadRGB323232F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.RGBA32323232F: return(TextureDecompressors.ReadRGBA32323232F(GetDecompressedBuffer(), Width, Height)); case VTexFormat.BC6H: return(BPTC.BPTCDecoders.UncompressBC6H(GetDecompressedBuffer(), Width, Height)); case VTexFormat.BC7: bool hemiOctRB = false; invert = false; if (Resource.EditInfo.Structs.ContainsKey(ResourceEditInfo.REDIStruct.SpecialDependencies)) { var specialDeps = (SpecialDependencies)Resource.EditInfo.Structs[ResourceEditInfo.REDIStruct.SpecialDependencies]; hemiOctRB = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version Mip HemiOctIsoRoughness_RG_B"); invert = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version LegacySource1InvertNormals"); } return(BPTC.BPTCDecoders.UncompressBC7(GetDecompressedBuffer(), Width, Height, hemiOctRB, invert)); case VTexFormat.ATI2N: normalize = false; if (Resource.EditInfo.Structs.ContainsKey(ResourceEditInfo.REDIStruct.SpecialDependencies)) { var specialDeps = (SpecialDependencies)Resource.EditInfo.Structs[ResourceEditInfo.REDIStruct.SpecialDependencies]; normalize = specialDeps.List.Any(dependancy => dependancy.CompilerIdentifier == "CompileTexture" && dependancy.String == "Texture Compiler Version Image NormalizeNormals"); } return(TextureDecompressors.UncompressATI2N(skiaBitmap, GetTextureSpan(), Width, Height, normalize)); case VTexFormat.IA88: return(TextureDecompressors.ReadIA88(skiaBitmap, GetTextureSpan())); case VTexFormat.ATI1N: return(TextureDecompressors.UncompressATI1N(skiaBitmap, GetTextureSpan(), Width, Height)); // TODO: Are we sure DXT5 and RGBA8888 are just raw buffers? case VTexFormat.JPEG_DXT5: case VTexFormat.JPEG_RGBA8888: case VTexFormat.PNG_DXT5: case VTexFormat.PNG_RGBA8888: return(ReadBuffer()); case VTexFormat.ETC2: // TODO: Rewrite EtcDecoder to work on skia span directly var etc = new Etc.EtcDecoder(); var data = new byte[skiaBitmap.RowBytes * skiaBitmap.Height]; etc.DecompressETC2(GetDecompressedTextureAtMipLevel(0), width, height, data); var gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned); skiaBitmap.InstallPixels(skiaBitmap.Info, gcHandle.AddrOfPinnedObject(), skiaBitmap.RowBytes, (address, context) => { gcHandle.Free(); }, null); break; case VTexFormat.ETC2_EAC: // TODO: Rewrite EtcDecoder to work on skia span directly var etc2 = new Etc.EtcDecoder(); var data2 = new byte[skiaBitmap.RowBytes * skiaBitmap.Height]; etc2.DecompressETC2A8(GetDecompressedTextureAtMipLevel(0), width, height, data2); var gcHandle2 = GCHandle.Alloc(data2, GCHandleType.Pinned); skiaBitmap.InstallPixels(skiaBitmap.Info, gcHandle2.AddrOfPinnedObject(), skiaBitmap.RowBytes, (address, context) => { gcHandle2.Free(); }, null); break; case VTexFormat.BGRA8888: return(TextureDecompressors.ReadBGRA8888(skiaBitmap, GetTextureSpan())); default: throw new NotImplementedException(string.Format("Unhandled image type: {0}", Format)); } return(skiaBitmap); }