class new in 2019.10
BasisImporterBasis Universal importer plugin.
Supports Basis Universal compressed images (*.basis
or *.ktx2
) by parsing and transcoding files into an explicitly specified GPU format (see Target format). You can use BasisImageConverter to transcode images into this format.
This plugin provides BasisImporterEacR
, BasisImporterEacRG
, BasisImporterEtc1RGB
, BasisImporterEtc2RGBA
, BasisImporterBc1RGB
, BasisImporterBc3RGBA
, BasisImporterBc4R
, BasisImporterBc5RG
, BasisImporterBc7RGBA
, BasisImporterPvrtc1RGB4bpp
, BasisImporterPvrtc1RGBA4bpp
, BasisImporterAstc4x4RGBA
, BasisImporterRGBA8
.
Usage
This plugin depends on the Trade and Basis Universal libraries and is built if MAGNUM_WITH_BASISIMPORTER
is enabled when building Magnum Plugins. To use as a dynamic plugin, load "BasisImporter"
via Corrade::v1_15_update2
tag, but could possibly compile against newer versions as well.
Additionally, if you're using Magnum as a CMake subproject, bundle the magnum-plugins and basis-universal repositories and do the following:
set(BASIS_UNIVERSAL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/basis-universal) set(MAGNUM_WITH_BASISIMPORTER ON CACHE BOOL "" FORCE) add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) # So the dynamically loaded plugin gets built implicitly add_dependencies(your-app MagnumPlugins::BasisImporter)
To use as a static plugin or as a dependency of another plugin with CMake, put FindMagnumPlugins.cmake and FindBasisUniversal.cmake into your modules/
directory, request the BasisImporter
component of the MagnumPlugins
package and link to the MagnumPlugins::BasisImporter
target:
find_package(MagnumPlugins REQUIRED BasisImporter) # ... target_link_libraries(your-app PRIVATE MagnumPlugins::BasisImporter)
See Downloading and building plugins, Plugin usage with CMake, Loading and using plugins and File format support for more information.
Behavior and limitations
The importer recognizes ImporterFlag::
Image types
You can import all image types supported by basisu
: (layered) 2D images, (layered) cube maps, 3D images and videos. They can in turn all have multiple mip levels. The images are annotated with ImageFlag3D::
For layered 2D images and (layered) cube maps, the array layers and faces are exposed as an additional image dimension. image3D() will return an ImageData3D with n z-slices, or 6*n z-slices for cube maps.
All 3D images will be imported as 2D array textures with as many layers as depth slices. This unifies the behaviour with Basis compressed KTX2 files that don't support 3D images in the first place, and avoids confusing behaviour with mip levels which are always 2-dimensional in Basis compressed images.
Video files will be imported as multiple 2D images with the same size and level count. Due to the way video is encoded by Basis Universal, seeking to arbitrary frames is not allowed. If you call image2D() with non-sequential frame indices and that frame is not an I-frame, it will print an error and fail. Restarting from frame 0 is always allowed.
Multilevel images
Files with multiple mip levels are imported with the largest level first, with the size of each following level divided by 2, rounded down. Mip chains can be incomplete, ie. they don't have to extend all the way down to a level of size 1x1.
Because mip levels in .basis
files are always 2-dimensional, they wouldn't halve correctly in the z-dimension for 3D images. If a 3D image with mip levels is detected, it gets imported as a layered 2D image instead, along with a warning being printed.
Cube maps
Cube map faces are imported in the order +X, -X, +Y, -Y, +Z, -Z as seen from a left-handed coordinate system (+X is right, +Y is up, +Z is forward). Layered cube maps are stored as multiple sets of faces, ie. all faces +X through -Z for the first layer, then all faces of the second layer, etc.
KTX2 files
Basis Universal supports only the Basis-encoded subset of the KTX2 format. It treats non-Basis-encoded KTX2 files the same way as broken KTX2 files, and so the plugin cannot robustly proxy the loading to KtxImporter in that case. Instead, if you're dealing with generic KTX2 files, you're encouraged to use KtxImporter directly — it will then delegate to BasisImporter for Basis-encoded files.
Plugin-specific configuration
Basis allows configuration of the format of loaded compressed data. The full form of the configuration is shown below:
[configuration] # No format is specified by default and you have to choose one either by # changing this value or by loading the plugin under an alias. See class # documentation for more information. format=
See Editing plugin-specific configuration for more information and an example showing how to edit the configuration values.
Target format
Basis is a compressed format that is transcoded into a compressed GPU format. With BasisImporter, this format can be chosen in different ways:
/* Choose ETC2 target format */ Containers::Pointer<Trade::AbstractImporter> importerEtc2 = manager.instantiate("BasisImporterEtc2"); /* Choose BC5 target format */ Containers::Pointer<Trade::AbstractImporter> importerBc5 = manager.instantiate("BasisImporterBc5");
The list of valid suffixes is equivalent to enum value names in TargetFormat. If you want to be able to change the target format dynamically, you may want to set the format
configuration of the plugin, as shown below. If you instantiate this class directly without a plugin manager, you may also use setTargetFormat().
/* Instantiate the plugin under its default name. At this point, the plugin would decompress to full RGBA8, which is usually not what you want. */ Containers::Pointer<Trade::AbstractImporter> importer = manager.instantiate("BasisImporter"); importer->openFile("mytexture.basis"); /* Transcode the image to BC5 */ importer->configuration().setValue("format", "Bc5"); image = importer->image2D(0); // ... /* Transcode the same image, but to ETC2 now */ importer->configuration().setValue("format", "Etc2"); image = importer->image2D(0); // ...
There are many options and you should generally be striving for the highest-quality format available on a given platform. A detailed description of the choices can be found in the Basis Universal Wiki. As an example, the following code is a decision making used by magnum-player based on availability of corresponding OpenGL, OpenGL ES and WebGL extensions, in its full ugly glory:
if(PluginManager::PluginMetadata* metadata = manager.metadata("BasisImporter")) { GL::Context& context = GL::Context::current(); using namespace GL::Extensions; #ifdef MAGNUM_TARGET_WEBGL if(context.isExtensionSupported<WEBGL::compressed_texture_astc>()) #else if(context.isExtensionSupported<KHR::texture_compression_astc_ldr>()) #endif { metadata->configuration().setValue("format", "Astc4x4RGBA"); } #ifdef MAGNUM_TARGET_GLES else if(context.isExtensionSupported<EXT::texture_compression_bptc>()) #else else if(context.isExtensionSupported<ARB::texture_compression_bptc>()) #endif { metadata->configuration().setValue("format", "Bc7RGBA"); } #ifdef MAGNUM_TARGET_WEBGL else if(context.isExtensionSupported<WEBGL::compressed_texture_s3tc>()) #elif defined(MAGNUM_TARGET_GLES) else if(context.isExtensionSupported<EXT::texture_compression_s3tc>() || context.isExtensionSupported<ANGLE::texture_compression_dxt5>()) #else else if(context.isExtensionSupported<EXT::texture_compression_s3tc>()) #endif { metadata->configuration().setValue("format", "Bc3RGBA"); } else /* ES3 (but not WebGL 2) has ETC always, so none of these ifs is there */ #ifdef MAGNUM_TARGET_WEBGL if(context.isExtensionSupported<WEBGL::compressed_texture_etc>()) #elif defined(MAGNUM_TARGET_GLES2) if(context.isExtensionSupported<ANGLE::compressed_texture_etc>()) #elif !defined(MAGNUM_TARGET_GLES) if(context.isExtensionSupported<ARB::ES3_compatibility>()) #endif { metadata->configuration().setValue("format", "Etc2RGBA"); } /* On ES2 or WebGL fall back to PVRTC if ETC2 is not available */ #if defined(MAGNUM_TARGET_GLES2) || defined(MAGNUM_TARGET_WEBGL) #ifdef MAGNUM_TARGET_WEBGL else if(context.isExtensionSupported<WEBGL::compressed_texture_pvrtc>()) #else else if(context.isExtensionSupported<IMG::texture_compression_pvrtc>()) #endif { metadata->configuration().setValue("format", "PvrtcRGBA4bpp"); } #endif /* And then, for everything except ES3 (but not WebGL 2) which already stopped at ETC, fall back to uncompressed */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) || defined(MAGNUM_TARGET_WEBGL) else { /* Fall back to uncompressed if nothing else is supported */ metadata->configuration().setValue("format", "RGBA8"); } #endif }
Reducing binary size
To reduce the binary size of the transcoder, Basis Universal supports a set of preprocessor defines to turn off unneeded features. The Basis Universal Wiki lists macros to disable specific target formats as well as KTX2 support including the Zstd dependency. If you're building it from source with BASIS_UNIVERSAL_DIR
set, add the desired defines before adding magnum-plugins
as a subfolder:
add_definitions( -DBASISD_SUPPORT_BC7=0 -DBASISD_SUPPORT_KTX2=0) # ... add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL)
Base classes
- class AbstractImporter
- Base for importer plugins.
Public types
- enum class TargetFormat: UnsignedInt { Etc1RGB = 0, Etc2RGBA = 1, Bc1RGB = 2, Bc3RGBA = 3, Bc4R = 4, Bc5RG = 5, Bc7RGBA = 6, PvrtcRGB4bpp = 8, PvrtcRGBA4bpp = 9, Astc4x4RGBA = 10, RGBA8 = 13, EacR = 20, EacRG = 21 }
- Type to transcode to.
Public static functions
- static void initialize()
- Initialize Basis transcoder.
Constructors, destructors, conversion operators
- BasisImporter() explicit
- Default constructor.
-
BasisImporter(PluginManager::
AbstractManager& manager, const Containers:: StringView& plugin) explicit - Plugin manager constructor.
Public functions
- auto targetFormat() const -> TargetFormat
- Target format.
- void setTargetFormat(TargetFormat format)
- Set the target format.
Enum documentation
enum class Magnum:: Trade:: BasisImporter:: TargetFormat: UnsignedInt
Type to transcode to.
If the image does not contain an alpha channel and the target format has it, alpha will be set to opaque. Conversely, for output formats without alpha the channel will be dropped.
Function documentation
static void Magnum:: Trade:: BasisImporter:: initialize()
Initialize Basis transcoder.
If the class is instantiated directly (not through a plugin manager), this function has to be called explicitly before using any instance.
void Magnum:: Trade:: BasisImporter:: setTargetFormat(TargetFormat format)
Set the target format.
See Target format for more information.