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.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;

bool ReadAndTranscodeImage(const std::string& filePath, PVRTexture& output)
{
  // 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
  PVRTexture texture(filePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    return false;

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

  if (!texture.Transcode(RGBA8888,
        PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm,
        PVRTexLibColourSpace::PVRTLCS_Linear))
    return false;

  // 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).

  output = std::move(texture);
  return true;
}

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.

Note

Pre-processing functions do not work with compressed or packed pixel formats, therefore any texture passed into a pre-processing function must first be decompressed or unpacked, if necessary. This requirement does not apply to the Transcode(...) function.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;

bool ApplySomeProcessing(const std::string& inFilePath, const std::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
  PVRTexture texture(inFilePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    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() & PVRTEX_PFHIGHMASK)
      || texture.TextureHasPackedChannelData())
  {
    // Decompress texture to the standard RGBA8888 format.
    // Note: Any decompressed, non-packed pixel format would work here,
    // for example: R32G32B32A32 Signed float
    const PVRTuint64 RGBA8888 = PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

    if (!texture.Transcode(RGBA8888,
        PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm,
        PVRTexLibColourSpace::PVRTLCS_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::PVRTLRM_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(
    PVRTexLibPixelFormat::PVRTLPF_PVRTCI_4bpp_RGB,
    PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm,
    PVRTexLibColourSpace::PVRTLCS_Linear,
    PVRTexLibCompressorQuality::PVRTLCQ_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.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;

bool ResizeCanvasAndGenMipChain(
  const std::string& inFilePath,
  const std::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
  PVRTexture texture(inFilePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    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() & PVRTEX_PFHIGHMASK)
    || texture.TextureHasPackedChannelData())
  {
    // Decompress texture to the standard RGBA8888 format.
    // Note: Any decompressed, non-packed pixel format would work here,
    // for example: R32G32B32A32 Signed float
    const PVRTuint64 RGBA8888 = PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

    if (!texture.Transcode(RGBA8888,
      PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm,
      PVRTexLibColourSpace::PVRTLCS_Linear))
      return false;
  }

  // Generate MIP-map chain
  if (!texture.GenerateMIPMaps(PVRTexLibResizeMode::PVRTLRM_Linear))
    return false;

  // Resize canvas
  if (!texture.ResizeCanvas(512U, 256U, 1U, 0U, 0U, 0U))
    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.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;

bool CreateTextureFromPixelData(const std::string& outFilePath)
{
  // Create a texture header for a RGBA8888 image of 512x256x1 pixels,
  // with 1 mip level, 1 array and 1 face.
  const PVRTuint64 RGBA8888 = PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);
  const PVRTuint32 width = 512U;
  const PVRTuint32 height = 256U;
  const PVRTuint32 depth = 1U;
  const PVRTuint32 numMipMaps = 1U;
  const PVRTuint32 numArrayMembers = 1U;
  const PVRTuint32 numFaces = 1U;
  const PVRTextureHeader textureHeader(
    RGBA8888,
    width, height, depth,
    numMipMaps, numArrayMembers, numFaces);
  const PVRTuint32 textureSize = textureHeader.GetTextureDataSize();

  if (!textureSize)
    return false;

  // Create a buffer to temporarily hold the pixel data
  std::unique_ptr textureData(new PVRTuint8[textureSize]);

  /*
    Fill in texture data...
  */

  // Create a new texture from the header and pixel data.
  PVRTexture texture(textureHeader, textureData.get());
  return texture.SaveToFile(outFilePath);
}

Reading Metadata#

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

#include "PVRTexLib.hpp"
using namespace pvrtexlib;
#include <iostream>
#include <iomanip>

bool ReadTextureMetaData(const std::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
  PVRTexture texture(inFilePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    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 PVRTuint32 key = PVRTexLibMetaData::PVRTLMD_TextureOrientation;
  const PVRTuint32 devFOURCC = 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 (texture.GetMetaDataBlock(key, metaData, devFOURCC)
    && metaData.u32DataSize)
  {
    // Do something...
  }

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

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

  if (dataCount)
  {
    for (PVRTuint32 n = 0U; n < dataCount; ++n)
    {
      // Do something...
      std::cout << std::setprecision(3) << "Atlas Data (" << n << "): "
        << atlasData[n] << std::endl;
    }
  }

  // ...also the texture cubemap order is easily queryable
  const std::array cubeOrder = texture.GetTextureCubeMapOrder();
  std::cout << "Cubemap order: " << cubeOrder.data() << std::endl;
  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.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;

bool WriteMetaData(const std::string& inFilePath, const std::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
  PVRTexture texture(inFilePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    return false;

  // Meta data size in bytes
  constexpr PVRTuint32 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;
  metaData.DevFOURCC = PVRTEX_CURR_IDENT;
  metaData.u32Key = PVRTLMD_TextureOrientation;
  metaData.u32DataSize = META_DATA_SIZE;
  metaData.Data.reset(new PVRTuint8[META_DATA_SIZE]);

  metaData.Data[PVRTexLibAxis::PVRTLA_X] = PVRTexLibOrientation::PVRTLO_Left;
  metaData.Data[PVRTexLibAxis::PVRTLA_Y] = PVRTexLibOrientation::PVRTLO_Up;
  metaData.Data[PVRTexLibAxis::PVRTLA_Z] = PVRTexLibOrientation::PVRTLO_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.

Note

The memory backing the data pointer returned from GetTextureDataPointer(…) is tied to the lifetime of the PVRTexture object.

#include "PVRTexLib.hpp"
using namespace pvrtexlib;
#include <stdexcept>

bool AccessingPixelDataDirectly(
  const std::string& inFilePath,
  const std::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
  PVRTexture texture(inFilePath);

  // Check that PVRTexLib loaded the file successfully
  if (!texture.GetTextureDataSize())
    return false;

  // Check the input texture format to determine if the data is compressed
  // or the format is packed.
  if (!(texture.GetTexturePixelFormat() & PVRTEX_PFHIGHMASK)
    || 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
    const PVRTuint64 RGBA8888 = PVRTGENPIXELID4('r', 'g', 'b', 'a', 8, 8, 8, 8);

    if (!texture.Transcode(RGBA8888,
      PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm,
      PVRTexLibColourSpace::PVRTLCS_Linear))
      return false;
   }

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

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

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

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

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

          // All pixels in this surface
          for (PVRTuint32 pixel = 0U; pixel < numPixels; ++pixel)
          {
            switch (channelType)
            {
            case PVRTexLibVariableType::PVRTLVT_UnsignedByteNorm:
            case PVRTexLibVariableType::PVRTLVT_UnsignedByte:
            {
              DoSomethingWithPixel<PVRTuint8>(numChannels, data);
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_SignedByteNorm:
            case PVRTexLibVariableType::PVRTLVT_SignedByte:
            {
              DoSomethingWithPixel<PVRTint8>(numChannels, reinterpret_cast<PVRTint8>(data));
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_UnsignedShortNorm:
            case PVRTexLibVariableType::PVRTLVT_UnsignedShort:
            {
              DoSomethingWithPixel<PVRTuint16>(numChannels, reinterpret_cast<PVRTuint16>(data));
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_SignedShortNorm:
            case PVRTexLibVariableType::PVRTLVT_SignedShort:
            {
              DoSomethingWithPixel<PVRTint16>(numChannels, reinterpret_cast<PVRTint16>(data));
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_UnsignedIntegerNorm:
            case PVRTexLibVariableType::PVRTLVT_UnsignedInteger:
            {
              DoSomethingWithPixel<PVRTuint32>(numChannels, reinterpret_cast<PVRTuint32>(data));
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_SignedIntegerNorm:
            case PVRTexLibVariableType::PVRTLVT_SignedInteger:
            {
              DoSomethingWithPixel<PVRTint32>(numChannels, reinterpret_cast<PVRTint32>(data));
              break;
            }
            case PVRTexLibVariableType::PVRTLVT_SignedFloat:
            case PVRTexLibVariableType::PVRTLVT_UnsignedFloat:
            {
              DoSomethingWithPixel<float>(numChannels, reinterpret_cast<float>(data));
              break;
            }
            default: throw std::invalid_argument("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);
}