PVRTexLib.NET 1.0.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package PVRTexLib.NET --version 1.0.2                
NuGet\Install-Package PVRTexLib.NET -Version 1.0.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PVRTexLib.NET" Version="1.0.2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add PVRTexLib.NET --version 1.0.2                
#r "nuget: PVRTexLib.NET, 1.0.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install PVRTexLib.NET as a Cake Addin
#addin nuget:?package=PVRTexLib.NET&version=1.0.2

// Install PVRTexLib.NET as a Cake Tool
#tool nuget:?package=PVRTexLib.NET&version=1.0.2                

PVRTexLib.NET

Introduction

PVRTexLib.NET is a wrapper for PVRTexLib supporting .NET Standard 2.0 and upper. It contains all the API from PVRTexLib.
The documents can be found through installing PVRTexTool.

License

PVRTexLib.NET is licensed under the MIT license.

Quick Start

Create PVRTexture instance to handle your textures.

using PVRTexLib;

class TextureTranscoder
{
    static void Main(string[] args)
    {
        EncodeTexture("D:\\in.png", "D:\\out.bin", (ulong)PVRTexLibPixelFormat.PVRTLPF_PVRTCII_HDR_8bpp);
        DecodeTexture("D:\\out.bin", "D:\\in2.png");
    }
    
    public static unsafe void EncodeTexture(string inFile, string outFile, ulong outFormat)
    {
        using (Bitmap bitmap = new Bitmap(Image.FromFile(inFile)))
        {
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            using (PVRTextureHeader header = new PVRTextureHeader(PVRDefine.PVRTGENPIXELID4('b', 'g', 'r', 'a', 8, 8, 8, 8), (uint)bitmap.Width, (uint)bitmap.Height, 1, 1, 1, 1, PVRTexLibColourSpace.PVRTLCS_sRGB, PVRTexLibVariableType.PVRTLVT_UnsignedByteNorm, false))
            {
                using (PVRTexture tex = new PVRTexture(header, (void*)data.Scan0))
                {
                    if (tex.GetTextureDataSize() != 0)
                    {
                        if (tex.Transcode(outFormat, PVRTexLibVariableType.PVRTLVT_UnsignedByteNorm, PVRTexLibColourSpace.PVRTLCS_sRGB, 0, false))
                        {
                            using (Stream outStream = File.Create(outFile))
                            {
                                using (BinaryWriter bw = new BinaryWriter(outStream))
                                {
                                    bw.Write(bitmap.Width);
                                    bw.Write(bitmap.Height);
                                    bw.Write(outFormat);
                                    bw.Write(new ReadOnlySpan<byte>(tex.GetTextureDataPointer(0), (int)tex.GetTextureDataSize(0)));
                                }
                            }
                        }
                    }
                }
            }
            bitmap.UnlockBits(data);
        }
    }

    public static unsafe void DecodeTexture(string inFile, string outFile)
    {
        ulong size = 0ul;
        byte[] rawTexDataArray;
        int width, height;
        ulong inFormat;
        using (Stream inStream = File.OpenRead(inFile))
        {
            using (BinaryReader br = new BinaryReader(inStream))
            {
                width = br.ReadInt32();
                height = br.ReadInt32();
                inFormat = br.ReadUInt64();
                size = (ulong)(inStream.Length - 16);
                rawTexDataArray = new byte[size];
                br.Read(rawTexDataArray);
            }
        }
        using (Bitmap bitmap = new Bitmap(width, height))
        {
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            using (PVRTextureHeader header = new PVRTextureHeader(inFormat, (uint)width, (uint)height, 1, 1, 1, 1, PVRTexLibColourSpace.PVRTLCS_sRGB, PVRTexLibVariableType.PVRTLVT_UnsignedByteNorm, false))
            {
                fixed (byte* rawTexDataPtr = &rawTexDataArray[0])
                {
                    using (PVRTexture tex = new PVRTexture(header, rawTexDataPtr))
                    {
                        if (tex.GetTextureDataSize() != 0)
                        {
                            if (tex.Transcode(PVRDefine.PVRTGENPIXELID4('b', 'g', 'r', 'a', 8, 8, 8, 8), PVRTexLibVariableType.PVRTLVT_UnsignedByteNorm, PVRTexLibColourSpace.PVRTLCS_sRGB, 0, false))
                            {
                                NativeMemory.Copy(tex.GetTextureDataPointer(0), (void*)data.Scan0, (nuint)tex.GetTextureDataSize(0));
                            }
                        }
                    }
                }
            }
            bitmap.UnlockBits(data);
            bitmap.Save(outFile);
        }
    }
}

Examples of Using the PVRTexTool Library

This section demonstrates a few of examples of using the PVRTexTool library with the required code shown.

Reading and Transcoding an Image

In this example, an existing file is read from disk and then transcoded to RGBA8888. The resulting texture object can then be used for later processing.

public static PVRTexture? ReadAndTranscodeImage(string filePath)
{
    // Open and read a texture from the file location specified by filePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(filePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return null;
    }

    // Decompress texture to the standard RGBA8888 format.
    ulong RGBA8888 = PVRDefine.PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

    if (!texture.Transcode(RGBA8888, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.Linear))
    {
        return null;
    }

    // texture is now in the format RGBA8888,
    // with each channel being of type unsigned integer,
    // and in linear colour space (i.e. without gamma correction).
    return new PVRTexture(in texture);
}

Pre-processing a Texture

In this example, a file is loaded, the pixel format is checked to determine if it is compressed or packed and transcoded to a usable format if required. The texture is then converted into a normal map of the same dimensions and a full MIP map chain is generated. Finally the texture is encoded into PVRTC1 four bits per pixel and saved to an output file.
The memory backing the data pointer returned from GetTextureDataPointer(...) is tied to the lifetime of the PVRTexture object.

public static bool ApplySomeProcessing(string inFilePath, string outFilePath)
{
    // Open and read a texture from the file location specified by inFilePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(inFilePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return false;
    }

    // Pre-processing functions will not with some formats so
    // check the input texture format to determine if the data is compressed
    // or the format is packed.
    if ((texture.GetTexturePixelFormat() & PVRDefine.PVRTEX_PFHIGHMASK) == 0 || texture.TextureHasPackedChannelData())
    {
        // Decompress texture to the standard RGBA8888 format.
        // Note: Any decompressed, non-packed pixel format would work here,
        // for example: R32G32B32A32 Signed float
        ulong RGBA8888 = PVRDefine.PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

        if (!texture.Transcode(RGBA8888, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.Linear))
        {
            return false;
        }
    }

    // Convert the image to a Normal Map with a scale of 5.0, and y/z/x channel order
    if (!texture.GenerateNormalMap(5.0f, "yzx"))
    {
        return false;
    }

    // Generate MIP-map chain
    if (!texture.GenerateMIPMaps(PVRTexLibResizeMode.Linear))
    {
        return false;
    }

    // Compress to PVRTC 4bpp.
    // Note: A better compressor quality will improve image quality,
    // at the expensive of compression speed.
    if (!texture.Transcode((ulong)PVRTexLibPixelFormat.PVRTCI_4bpp_RGB, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.Linear, PVRTexLibCompressorQuality.PVRTCNormal))
    {
        return false;
    }

    // Save the texture to file location specified by outFilePath.
    // The file type will be determined by the extension present in the string.
    // Valid extensions are: PVR, KTX, KTX2, ASTC, DDS and h
    // If no extension is present the PVR format will be selected.
    return texture.SaveToFile(outFilePath);
}

Reading an Image and Resizing the Canvas

In this example, an existing file is opened, the pixel format is checked to determine if it is compressed or packed and transcoded to a usable format if required. The canvas is resized to 512x256, leaving the (original) canvas in the top left of the texture. A full MIP map chain is then generated and the texture is saved to a file.

public static bool ResizeCanvasAndGenMipChain(string inFilePath, string outFilePath)
{
    // Open and read a texture from the file location specified by inFilePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(inFilePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return false;
    }

    // Pre-processing functions will not with some formats so
    // check the input texture format to determine if the data is compressed
    // or the format is packed.
    if ((texture.GetTexturePixelFormat() & PVRDefine.PVRTEX_PFHIGHMASK) == 0 || texture.TextureHasPackedChannelData())
    {
        // Decompress texture to the standard RGBA8888 format.
        // Note: Any decompressed, non-packed pixel format would work here,
        // for example: R32G32B32A32 Signed float
        ulong RGBA8888 = PVRDefine.PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

        if (!texture.Transcode(RGBA8888, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.Linear))
        {
            return false;
        }
    }

    // Generate MIP-map chain
    if (!texture.GenerateMIPMaps(PVRTexLibResizeMode.Linear))
    {
        return false;
    }

    // Resize canvas
    if (!texture.ResizeCanvas(512U, 256U, 1U, 0, 0, 0))
    {
        return false;
    }

    // Save the texture to file location specified by outFilePath.
    // The file type will be determined by the extension present in the string.
    // Valid extensions are: PVR, KTX, KTX2, ASTC, DDS and h
    // If no extension is present the PVR format will be selected.
    return texture.SaveToFile(outFilePath);
}

Creating an Image from Pixel Data

In this example, we show how to create a texture from raw pixel data and a PVRTextureHeader object, the resulting texture is then saved to a file.

public static bool CreateTextureFromPixelData(string outFilePath)
{
    // Create a texture header for a RGBA8888 image of 512x256x1 pixels,
    // with 1 mip level, 1 array and 1 face.
    ulong RGBA8888 = PVRDefine.PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);
    uint width = 512U;
    uint height = 256U;
    uint depth = 1U;
    uint numMipMaps = 1U;
    uint numArrayMembers = 1U;
    uint numFaces = 1U;
    using PVRTextureHeader textureHeader = new PVRTextureHeader(RGBA8888, width, height, depth, numMipMaps, numArrayMembers, numFaces);
    ulong textureSize = textureHeader.GetTextureDataSize();

    if (textureSize == 0)
    {
        return false;
    }

    // Create a buffer to temporarily hold the pixel data
    byte[] textureData = new byte[textureSize];

    /*
        Fill in texture data...
    */

    // Create a new texture from the header and pixel data.
    unsafe
    {
        fixed (byte* ptr = &textureData[0])
        {
            using PVRTexture texture = new PVRTexture(textureHeader, ptr);
            return texture.SaveToFile(outFilePath);
        }
    }
}

Reading Metadata

In this example we show how to use the API to read meta data from a PVRTexture object.

public static bool ReadTextureMetaData(string inFilePath)
{
    // Open and read a texture from the file location specified by inFilePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(inFilePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return false;
    }

    // Attempt to retrieve the meta data for the textures orientation
    // You may choose your own values for devFOURCC and key.
    // In this case we are using the PVR FOURCC and
    // PVRTexLibMetaData::PVRTLMD_TextureOrientation.
    MetaDataBlock metaData;
    const uint key = (uint)PVRTexLibMetaData.TextureOrientation;
    const uint devFOURCC = PVRDefine.PVRTEX_CURR_IDENT;

    // Check the call was successful
    // If the meta data doesn't exist, a block with a data size
    // of 0 will be returned.
    if ((metaData = texture.GetMetaDataBlock(key, devFOURCC)) != null && metaData.u32DataSize != 0)
    {
        // Do something...
    }

    // PVRTexLib also has some baked in meta data accessors...
    // For examaple direct data access to texture atlas meta data.
    unsafe
    {
        uint dataCount;
        float* atlasData = texture.GetTextureAtlasData(&dataCount);

        // Note: The memory backing 'atlasData' is tied to the
        // lifetime of the 'texture' object.

        if (dataCount != 0)
        {
            for (uint n = 0U; n < dataCount; n++)
            {
                Console.WriteLine("Atlas Data ({0}): {1}", n, atlasData[n]);
            }
        }
    }

    // ...also the texture cubemap order is easily queryable
    string cubeOrder = texture.GetTextureCubeMapOrder();
    Console.WriteLine("Cubemap order: {0}", cubeOrder);
    return true;
}

Writing Meta Data

In this example, an existing file is read from disk, we create a MetaDataBlock and then insert the meta data into the texture, finally we save the modified texture back to a file.

public static bool WriteMetaData(string inFilePath, string outFilePath)
{
    // Open and read a texture from the file location specified by inFilePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(inFilePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return false;
    }

    // Meta data size in bytes
    const uint META_DATA_SIZE = 3U;

    // Create meta data block
    // FourCC 'PVR', developers may use their own FourCC
    // Meta data key, in this case texture orientation
    MetaDataBlock metaData = new MetaDataBlock();
    metaData.DevFOURCC = PVRDefine.PVRTEX_CURR_IDENT;
    metaData.u32Key = (uint)PVRTexLibMetaData.TextureOrientation;
    metaData.u32DataSize = META_DATA_SIZE;
    metaData.Data = new byte[META_DATA_SIZE];

    metaData.Data[(int)PVRTexLibAxis.X] = (byte)PVRTexLibOrientation.Left;
    metaData.Data[(int)PVRTexLibAxis.Y] = (byte)PVRTexLibOrientation.Up;
    metaData.Data[(int)PVRTexLibAxis.Z] = (byte)PVRTexLibOrientation.Out;

    // Insert the meta data into the texture
    texture.AddMetaData(metaData);

    // Save the texture to file location specified by outFilePath.
    // The file type will be determined by the extension present in the string.
    // Valid extensions are: PVR, KTX, KTX2, ASTC, DDS and h
    // If no extension is present the PVR format will be selected.
    return texture.SaveToFile(outFilePath);
}

Accessing Pixel Data Directly

This example demonstrates how to use the GetTextureDataPointer(...) API which allows for direct data access. First a file is loaded, the pixel format is checked to determine if it is compressed or packed and transcoded to a usable format if required. Then each surface (Mip level, array member, face and Z slice) of the texture is iterated over, for each surface every pixel is iterated over, this allows for access to every pixel in the entire texture, each pixel can then be modified by the user, if desired.
The appropriate data type for accessing the pixel channel data can be determined via the GetTextureChannelType() API. The stride between each pixel can be calculated by querying the bits per pixel (via GetTextureBitsPerPixel() API) and diving by 8.
The memory backing the data pointer returned from GetTextureDataPointer(...) is tied to the lifetime of the PVRTexture object.

public static bool AccessingPixelDataDirectly(string inFilePath, string outFilePath)
{
    // Open and read a texture from the file location specified by inFilePath.
    // Accepted file formats are: PVR, KTX, KTX2, ASTC, DDS,
    // PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PPM, PGM and PIC
    using PVRTexture texture = new PVRTexture(inFilePath);

    // Check that PVRTexLib loaded the file successfully
    if (texture.GetTextureDataSize() == 0)
    {
        return false;
    }

    // Check the input texture format to determine if the data is compressed
    // or the format is packed.
    if ((texture.GetTexturePixelFormat() & PVRDefine.PVRTEX_PFHIGHMASK) == 0 || texture.TextureHasPackedChannelData())
    {
        // Dont want to deal with accessing compressed or packed data so first
        // transcode the texture to RGBA8888 format.
        // Note: Any decompressed, non-packed pixel format would work here,
        // for example: R32G32B32A32 Signed float
        ulong RGBA8888 = PVRDefine.PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

        if (!texture.Transcode(RGBA8888, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.Linear))
        {
            return false;
        }
    }

    uint mipLevelCount = texture.GetTextureNumMipMapLevels();
    uint arrayCount = texture.GetTextureNumArrayMembers();
    uint faceCount = texture.GetTextureNumFaces();
    PVRTexLibVariableType channelType = texture.GetTextureChannelType();
    uint bytesPerPixel = texture.GetTextureBitsPerPixel() / 8U;
    uint numChannels = texture.GetTextureChannelCount();

    // Loop over every surface in the texture.
    // All Mip levels
    unsafe
    {
        for (uint level = 0U; level < mipLevelCount; level++)
        {
            // Width and height for this Mip level
            uint levelWidth = texture.GetTextureWidth(level);
            uint levelHeight = texture.GetTextureHeight(level);

            // Number of pixel for this (2D) surface
            uint numPixels = levelWidth * levelHeight;

            // Dimension in the Z axis for this Mip level
            uint levelDepth = texture.GetTextureDepth(level);


            // All array members
            for (uint array = 0U; array < arrayCount; ++array)
            {
                // All faces
                for (uint face = 0U; face < faceCount; ++face)
                {
                    // All Z slices (3D textures)
                    for (uint slice = 0U; slice < levelDepth; ++slice)
                    {
                        byte* data = (byte*)texture.GetTextureDataPointer(level, array, array, slice);

                        // All pixels in this surface
                        for (uint pixel = 0U; pixel < numPixels; ++pixel)
                        {
                            switch (channelType)
                            {
                                case PVRTexLibVariableType.UnsignedByteNorm:
                                case PVRTexLibVariableType.UnsignedByte:
                                    {
                                        DoSomethingWithPixel(numChannels, (byte*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.SignedByteNorm:
                                case PVRTexLibVariableType.SignedByte:
                                    {
                                        DoSomethingWithPixel(numChannels, (sbyte*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.UnsignedShortNorm:
                                case PVRTexLibVariableType.UnsignedShort:
                                    {
                                        DoSomethingWithPixel(numChannels, (ushort*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.SignedShortNorm:
                                case PVRTexLibVariableType.SignedShort:
                                    {
                                        DoSomethingWithPixel(numChannels, (short*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.UnsignedIntegerNorm:
                                case PVRTexLibVariableType.UnsignedInteger:
                                    {
                                        DoSomethingWithPixel(numChannels, (uint*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.SignedIntegerNorm:
                                case PVRTexLibVariableType.SignedInteger:
                                    {
                                        DoSomethingWithPixel(numChannels, (int*)data);
                                        break;
                                    }
                                case PVRTexLibVariableType.SignedFloat:
                                case PVRTexLibVariableType.UnsignedFloat:
                                    {
                                        DoSomethingWithPixel(numChannels, (float*)data);
                                        break;
                                    }
                                default: throw new Exception("Unknown channel type");
                            }

                            data += bytesPerPixel;
                        }
                    }
                }
            }
        }
    }

    // Save the modified texture to the file location specified by outFilePath.
    // The file type will be determined by the extension present in the string.
    // Valid extensions are: PVR, KTX, KTX2, ASTC, DDS and h
    // If no extension is present the PVR format will be selected.
    return texture.SaveToFile(outFilePath);
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.
  • .NETStandard 2.1

    • No dependencies.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.3 270 5/8/2024
1.0.2 139 4/28/2024
1.0.1 149 4/27/2024
1.0.0 158 4/27/2024

1.修复回调函数报错问题
2.将枚举名称改为C#命名样式
3.在.NET 8.0使用LibraryImport特性