basisu: add bindings for basis-universal (supercompressed textures) (#477)
This commit is contained in:
parent
88558b7d52
commit
4a0802639c
13 changed files with 1075 additions and 0 deletions
3
basisu/.gitmodules
vendored
Normal file
3
basisu/.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "upstream"]
|
||||
path = upstream
|
||||
url = https://github.com/hexops/basisu
|
||||
124
basisu/build.zig
Normal file
124
basisu/build.zig
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
const basisu_root = thisDir() ++ "/upstream/basisu";
|
||||
|
||||
pub const pkg = std.build.Pkg{
|
||||
.name = "basisu",
|
||||
.source = .{
|
||||
.path = "src/main.zig",
|
||||
},
|
||||
};
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&testStep(b, mode, target).step);
|
||||
}
|
||||
|
||||
pub fn testStep(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *std.build.RunStep {
|
||||
const main_tests = b.addTestExe("basisu-tests", comptime thisDir() ++ "/src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
main_tests.setTarget(target);
|
||||
main_tests.main_pkg_path = thisDir();
|
||||
link(b, main_tests, .{
|
||||
.encoder = true,
|
||||
.transcoder = true,
|
||||
});
|
||||
main_tests.install();
|
||||
return main_tests.run();
|
||||
}
|
||||
|
||||
pub fn link(b: *Builder, step: *std.build.LibExeObjStep, options: Options) void {
|
||||
if (options.encoder) {
|
||||
step.linkLibrary(buildEncoder(b));
|
||||
step.addCSourceFile(comptime thisDir() ++ "/src/encoder/wrapper.cpp", &.{});
|
||||
step.addIncludeDir(basisu_root ++ "/encoder");
|
||||
}
|
||||
if (options.transcoder) {
|
||||
step.linkLibrary(buildTranscoder(b));
|
||||
step.addCSourceFile(comptime thisDir() ++ "/src/transcoder/wrapper.cpp", &.{});
|
||||
step.addIncludeDir(basisu_root ++ "/transcoder");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buildEncoder(b: *Builder) *std.build.LibExeObjStep {
|
||||
// TODO(build-system): https://github.com/hexops/mach/issues/229#issuecomment-1100958939
|
||||
ensureDependencySubmodule(b.allocator, "upstream") catch unreachable;
|
||||
|
||||
const encoder = b.addStaticLibrary("basisu-encoder", null);
|
||||
encoder.linkLibCpp();
|
||||
encoder.addCSourceFiles(
|
||||
encoder_sources,
|
||||
&.{},
|
||||
);
|
||||
|
||||
encoder.defineCMacro("BASISU_FORCE_DEVEL_MESSAGES", "0");
|
||||
encoder.defineCMacro("BASISD_SUPPORT_KTX2_ZSTD", "0");
|
||||
encoder.install();
|
||||
return encoder;
|
||||
}
|
||||
|
||||
pub fn buildTranscoder(b: *Builder) *std.build.LibExeObjStep {
|
||||
// TODO(build-system): https://github.com/hexops/mach/issues/229#issuecomment-1100958939
|
||||
ensureDependencySubmodule(b.allocator, "upstream") catch unreachable;
|
||||
|
||||
const transcoder = b.addStaticLibrary("basisu-transcoder", null);
|
||||
transcoder.linkLibCpp();
|
||||
transcoder.addCSourceFiles(
|
||||
transcoder_sources,
|
||||
&.{},
|
||||
);
|
||||
|
||||
transcoder.defineCMacro("BASISU_FORCE_DEVEL_MESSAGES", "0");
|
||||
transcoder.defineCMacro("BASISD_SUPPORT_KTX2_ZSTD", "0");
|
||||
transcoder.install();
|
||||
return transcoder;
|
||||
}
|
||||
|
||||
pub const Options = struct {
|
||||
encoder: bool,
|
||||
transcoder: bool,
|
||||
};
|
||||
|
||||
fn thisDir() []const u8 {
|
||||
return std.fs.path.dirname(@src().file) orelse ".";
|
||||
}
|
||||
|
||||
fn ensureDependencySubmodule(allocator: std.mem.Allocator, path: []const u8) !void {
|
||||
if (std.process.getEnvVarOwned(allocator, "NO_ENSURE_SUBMODULES")) |no_ensure_submodules| {
|
||||
if (std.mem.eql(u8, no_ensure_submodules, "true")) return;
|
||||
} else |_| {}
|
||||
var child = std.ChildProcess.init(&.{ "git", "submodule", "update", "--init", path }, allocator);
|
||||
child.cwd = (comptime thisDir());
|
||||
child.stderr = std.io.getStdErr();
|
||||
child.stdout = std.io.getStdOut();
|
||||
|
||||
_ = try child.spawnAndWait();
|
||||
}
|
||||
|
||||
const transcoder_sources = &[_][]const u8{
|
||||
basisu_root ++ "/transcoder/basisu_transcoder.cpp",
|
||||
};
|
||||
|
||||
const encoder_sources = &[_][]const u8{
|
||||
basisu_root ++ "/encoder/basisu_backend.cpp",
|
||||
basisu_root ++ "/encoder/basisu_basis_file.cpp",
|
||||
basisu_root ++ "/encoder/basisu_bc7enc.cpp",
|
||||
basisu_root ++ "/encoder/basisu_comp.cpp",
|
||||
basisu_root ++ "/encoder/basisu_enc.cpp",
|
||||
basisu_root ++ "/encoder/basisu_etc.cpp",
|
||||
basisu_root ++ "/encoder/basisu_frontend.cpp",
|
||||
basisu_root ++ "/encoder/basisu_gpu_texture.cpp",
|
||||
basisu_root ++ "/encoder/basisu_kernels_sse.cpp",
|
||||
basisu_root ++ "/encoder/basisu_opencl.cpp",
|
||||
basisu_root ++ "/encoder/basisu_pvrtc1_4.cpp",
|
||||
basisu_root ++ "/encoder/basisu_resample_filters.cpp",
|
||||
basisu_root ++ "/encoder/basisu_resampler.cpp",
|
||||
basisu_root ++ "/encoder/basisu_ssim.cpp",
|
||||
basisu_root ++ "/encoder/basisu_uastc_enc.cpp",
|
||||
basisu_root ++ "/encoder/jpgd.cpp",
|
||||
basisu_root ++ "/encoder/pvpngreader.cpp",
|
||||
};
|
||||
259
basisu/src/encoder.zig
Normal file
259
basisu/src/encoder.zig
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
const std = @import("std");
|
||||
const b = @import("encoder/binding.zig");
|
||||
const BasisTextureFormat = @import("main.zig").BasisTextureFormat;
|
||||
const testing = std.testing;
|
||||
|
||||
/// Must be called before encoding anything
|
||||
pub fn init_encoder() void {
|
||||
b.basisu_encoder_init();
|
||||
}
|
||||
|
||||
pub const Compressor = struct {
|
||||
pub const Error = error{
|
||||
InitializationFailed,
|
||||
ValidationFailed,
|
||||
EncodingUASTCFailed,
|
||||
CannotReadSourceImages,
|
||||
FrontendFault,
|
||||
FrontendExtractionFailed,
|
||||
BackendFault,
|
||||
CannotCreateBasisFile,
|
||||
CannotWriteOutput,
|
||||
UASTCRDOPostProcessFailed,
|
||||
CannotCreateKTX2File,
|
||||
};
|
||||
|
||||
handle: *b.Compressor,
|
||||
|
||||
pub fn init(params: CompressorParams) error{Unknown}!Compressor {
|
||||
return Compressor{
|
||||
.handle = if (b.compressor_init(params.handle)) |v| v else return error.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Compressor) void {
|
||||
b.compressor_deinit(self.handle);
|
||||
}
|
||||
|
||||
pub fn process(self: Compressor) Error!void {
|
||||
return switch (b.compressor_process(self.handle)) {
|
||||
0 => {},
|
||||
1 => error.InitializationFailed,
|
||||
2 => error.CannotReadSourceImages,
|
||||
3 => error.ValidationFailed,
|
||||
4 => error.EncodingUASTCFailed,
|
||||
5 => error.FrontendFault,
|
||||
6 => error.FrontendExtractionFailed,
|
||||
7 => error.BackendFault,
|
||||
8 => error.CannotCreateBasisFile,
|
||||
9 => error.UASTCRDOPostProcessFailed,
|
||||
10 => error.CannotCreateKTX2File,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// output will be freed with `Compressor.deinit`
|
||||
pub fn output(self: Compressor) []const u8 {
|
||||
return b.compressor_get_output(self.handle)[0..b.compressor_get_output_size(self.handle)];
|
||||
}
|
||||
|
||||
pub fn outputBitsPerTexel(self: Compressor) f64 {
|
||||
return b.compressor_get_output_bits_per_texel(self.handle);
|
||||
}
|
||||
|
||||
pub fn anyImageHasAlpha(self: Compressor) bool {
|
||||
return b.compressor_get_any_source_image_has_alpha(self.handle);
|
||||
}
|
||||
};
|
||||
|
||||
pub const CompressorParams = struct {
|
||||
handle: *b.CompressorParams,
|
||||
|
||||
pub fn init(threads_count: u32) CompressorParams {
|
||||
const h = CompressorParams{ .handle = b.compressor_params_init() };
|
||||
h.setStatusOutput(false);
|
||||
b.compressor_params_set_thread_count(h.handle, threads_count);
|
||||
return h;
|
||||
}
|
||||
|
||||
pub fn deinit(self: CompressorParams) void {
|
||||
b.compressor_params_deinit(self.handle);
|
||||
}
|
||||
|
||||
pub fn clear(self: CompressorParams) void {
|
||||
b.compressor_params_clear(self.handle);
|
||||
}
|
||||
|
||||
pub fn setStatusOutput(self: CompressorParams, enable_output: bool) void {
|
||||
b.compressor_params_set_status_output(self.handle, enable_output);
|
||||
}
|
||||
|
||||
/// `level` ranges from [1, 255]
|
||||
pub fn setQualityLevel(self: CompressorParams, level: u8) void {
|
||||
b.compressor_params_set_quality_level(self.handle, level);
|
||||
}
|
||||
|
||||
pub fn getPackUASTCFlags(self: CompressorParams) PackUASTCFlags {
|
||||
return PackUASTCFlags.from(b.compressor_params_get_pack_uastc_flags(self.handle));
|
||||
}
|
||||
|
||||
pub fn setPackUASTCFlags(self: CompressorParams, flags: PackUASTCFlags) void {
|
||||
b.compressor_params_set_pack_uastc_flags(self.handle, flags.cast());
|
||||
}
|
||||
|
||||
pub fn setBasisFormat(self: CompressorParams, format: BasisTextureFormat) void {
|
||||
b.compressor_params_set_uastc(self.handle, switch (format) {
|
||||
.etc1s => false,
|
||||
.uastc4x4 => true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn setColorSpace(self: CompressorParams, color_space: ColorSpace) void {
|
||||
b.compressor_params_set_perceptual(self.handle, switch (color_space) {
|
||||
.linear => false,
|
||||
.srgb => true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn setMipColorSpace(self: CompressorParams, color_space: ColorSpace) void {
|
||||
b.compressor_params_set_mip_srgb(self.handle, switch (color_space) {
|
||||
.linear => false,
|
||||
.srgb => true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Disable selector RDO, for faster compression but larger files.
|
||||
/// Enabled by default
|
||||
pub fn setSelectorRDO(self: CompressorParams, enable: bool) void {
|
||||
b.compressor_params_set_no_selector_rdo(self.handle, !enable);
|
||||
}
|
||||
|
||||
/// Enabled by default
|
||||
pub fn setEndpointRDO(self: CompressorParams, enable: bool) void {
|
||||
b.compressor_params_set_no_endpoint_rdo(self.handle, !enable);
|
||||
}
|
||||
|
||||
pub fn setRDO_UASTC(self: CompressorParams, enable: bool) void {
|
||||
b.compressor_params_set_rdo_uastc(self.handle, enable);
|
||||
}
|
||||
|
||||
pub fn setRDO_UASTCQualityScalar(self: CompressorParams, quality: f32) void {
|
||||
b.compressor_params_set_rdo_uastc_quality_scalar(self.handle, quality);
|
||||
}
|
||||
|
||||
pub fn setGenerateMipMaps(self: CompressorParams, enable: bool) void {
|
||||
b.compressor_params_set_generate_mipmaps(self.handle, enable);
|
||||
}
|
||||
|
||||
pub fn setMipSmallestDimension(self: CompressorParams, smallest_dimension: i32) void {
|
||||
b.compressor_params_set_mip_smallest_dimension(self.handle, smallest_dimension);
|
||||
}
|
||||
|
||||
/// Resizes sources list and creates a new Image in case index is out of bounds
|
||||
pub fn getImageSource(self: CompressorParams, index: u32) Image {
|
||||
return .{ .handle = b.compressor_params_get_or_create_source_image(self.handle, index) };
|
||||
}
|
||||
|
||||
pub fn resizeImageSource(self: CompressorParams, new_size: u32) void {
|
||||
b.compressor_params_resize_source_image_list(self.handle, new_size);
|
||||
}
|
||||
|
||||
pub fn clearImageSource(self: CompressorParams) void {
|
||||
b.compressor_params_clear_source_image_list(self.handle);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Image = struct {
|
||||
handle: *b.Image,
|
||||
|
||||
pub fn fill(self: Image, data: []const u8, w: u32, h: u32, channels: u3) void {
|
||||
b.compressor_image_fill(self.handle, data.ptr, w, h, channels);
|
||||
}
|
||||
|
||||
pub fn resize(self: Image, w: u32, h: u32, p: ?u32) void {
|
||||
b.compressor_image_resize(self.handle, w, h, p orelse std.math.maxInt(u32));
|
||||
}
|
||||
|
||||
pub fn width(self: Image) u32 {
|
||||
return b.compressor_image_get_width(self.handle);
|
||||
}
|
||||
|
||||
pub fn height(self: Image) u32 {
|
||||
return b.compressor_image_get_height(self.handle);
|
||||
}
|
||||
|
||||
pub fn pitch(self: Image) u32 {
|
||||
return b.compressor_image_get_pitch(self.handle);
|
||||
}
|
||||
|
||||
pub fn totalPixels(self: Image) u32 {
|
||||
return b.compressor_image_get_total_pixels(self.handle);
|
||||
}
|
||||
|
||||
pub fn blockWidth(self: Image, w: u32) u32 {
|
||||
return (self.width() + (w - 1)) / w;
|
||||
}
|
||||
|
||||
pub fn blockHeight(self: Image, h: u32) u32 {
|
||||
return (self.height() + (h - 1)) / h;
|
||||
}
|
||||
|
||||
pub fn totalBlocks(self: Image, w: u32, h: u32) u32 {
|
||||
return self.blockWidth(w) * self.blockHeight(h);
|
||||
}
|
||||
};
|
||||
|
||||
pub const PackUASTCFlags = packed struct {
|
||||
fastest: bool = false,
|
||||
faster: bool = false,
|
||||
default: bool = false,
|
||||
slower: bool = false,
|
||||
verySlow: bool = false,
|
||||
mask: bool = false,
|
||||
favor_uastc_error: bool = false,
|
||||
favor_bc7_error: bool = false,
|
||||
etc1_faster_hints: bool = false,
|
||||
etc1_fastest_hints: bool = false,
|
||||
etc1_disable_flip_and_individual: bool = false,
|
||||
favor_simpler_modes: bool = false,
|
||||
|
||||
pub const Flag = enum(u32) {
|
||||
fastest = 0,
|
||||
faster = 1,
|
||||
default = 2,
|
||||
slower = 3,
|
||||
verySlow = 4,
|
||||
mask = 0xF,
|
||||
favor_uastc_error = 8,
|
||||
favor_bc7_error = 16,
|
||||
etc1_faster_hints = 64,
|
||||
etc1_fastest_hints = 128,
|
||||
etc1_disable_flip_and_individual = 256,
|
||||
favor_simpler_modes = 512,
|
||||
};
|
||||
|
||||
pub fn from(bits: u32) PackUASTCFlags {
|
||||
var value = PackUASTCFlags{};
|
||||
inline for (comptime std.meta.fieldNames(Flag)) |field_name| {
|
||||
if (bits & (@enumToInt(@field(Flag, field_name))) != 0) {
|
||||
@field(value, field_name) = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn cast(self: PackUASTCFlags) u32 {
|
||||
var value: u32 = 0;
|
||||
inline for (comptime std.meta.fieldNames(Flag)) |field_name| {
|
||||
if (@field(self, field_name)) {
|
||||
value |= @enumToInt(@field(Flag, field_name));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
pub const ColorSpace = enum {
|
||||
linear,
|
||||
srgb,
|
||||
};
|
||||
42
basisu/src/encoder/binding.zig
Normal file
42
basisu/src/encoder/binding.zig
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
pub const Compressor = opaque {};
|
||||
pub const CompressorParams = opaque {};
|
||||
pub const Image = opaque {};
|
||||
|
||||
pub extern fn basisu_encoder_init() void;
|
||||
|
||||
pub extern fn compressor_params_init() *CompressorParams;
|
||||
pub extern fn compressor_params_deinit(*CompressorParams) void;
|
||||
pub extern fn compressor_params_clear(*CompressorParams) void;
|
||||
pub extern fn compressor_params_set_status_output(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_thread_count(*CompressorParams, u32) void;
|
||||
pub extern fn compressor_params_set_quality_level(*CompressorParams, c_int) void;
|
||||
pub extern fn compressor_params_get_pack_uastc_flags(*CompressorParams) u32;
|
||||
pub extern fn compressor_params_set_pack_uastc_flags(*CompressorParams, u32) void;
|
||||
pub extern fn compressor_params_set_uastc(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_perceptual(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_mip_srgb(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_no_selector_rdo(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_no_endpoint_rdo(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_rdo_uastc(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_rdo_uastc_quality_scalar(*CompressorParams, f32) void;
|
||||
pub extern fn compressor_params_set_generate_mipmaps(*CompressorParams, bool) void;
|
||||
pub extern fn compressor_params_set_mip_smallest_dimension(*CompressorParams, c_int) void;
|
||||
pub extern fn compressor_params_get_or_create_source_image(*CompressorParams, u32) *Image;
|
||||
pub extern fn compressor_params_resize_source_image_list(*CompressorParams, usize) void;
|
||||
pub extern fn compressor_params_clear_source_image_list(*CompressorParams) void;
|
||||
|
||||
pub extern fn compressor_image_fill(*Image, [*]const u8, u32, u32, u32) void;
|
||||
pub extern fn compressor_image_resize(*Image, u32, u32, u32) void;
|
||||
pub extern fn compressor_image_get_width(*Image) u32;
|
||||
pub extern fn compressor_image_get_height(*Image) u32;
|
||||
pub extern fn compressor_image_get_pitch(*Image) u32;
|
||||
pub extern fn compressor_image_get_total_pixels(*Image) u32;
|
||||
|
||||
pub extern fn compressor_init(*CompressorParams) ?*Compressor;
|
||||
pub extern fn compressor_deinit(*Compressor) void;
|
||||
pub extern fn compressor_process(*Compressor) u32;
|
||||
|
||||
pub extern fn compressor_get_output(*Compressor) [*]const u8;
|
||||
pub extern fn compressor_get_output_size(*Compressor) u32;
|
||||
pub extern fn compressor_get_output_bits_per_texel(*Compressor) f64;
|
||||
pub extern fn compressor_get_any_source_image_has_alpha(*Compressor) bool;
|
||||
176
basisu/src/encoder/wrapper.cpp
Normal file
176
basisu/src/encoder/wrapper.cpp
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#include <basisu_comp.h>
|
||||
#include <basisu_enc.h>
|
||||
#include <cstdbool>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
void basisu_encoder_init() { basisu::basisu_encoder_init(); }
|
||||
|
||||
basisu::basis_compressor_params *compressor_params_init() {
|
||||
return new basisu::basis_compressor_params();
|
||||
};
|
||||
|
||||
void compressor_params_deinit(basisu::basis_compressor_params *params) {
|
||||
delete params;
|
||||
}
|
||||
|
||||
void compressor_params_clear(basisu::basis_compressor_params *params) {
|
||||
params->clear();
|
||||
}
|
||||
|
||||
void compressor_params_set_status_output(
|
||||
basisu::basis_compressor_params *params, bool status_output) {
|
||||
params->m_status_output = status_output;
|
||||
}
|
||||
|
||||
void compressor_params_set_thread_count(basisu::basis_compressor_params *params,
|
||||
uint32_t thread_count) {
|
||||
params->m_pJob_pool = new basisu::job_pool(thread_count);
|
||||
}
|
||||
|
||||
void compressor_params_set_quality_level(
|
||||
basisu::basis_compressor_params *params, uint8_t quality_level) {
|
||||
params->m_quality_level = quality_level;
|
||||
}
|
||||
|
||||
uint32_t compressor_params_get_pack_uastc_flags(
|
||||
basisu::basis_compressor_params *params) {
|
||||
return params->m_pack_uastc_flags;
|
||||
}
|
||||
|
||||
void compressor_params_set_pack_uastc_flags(
|
||||
basisu::basis_compressor_params *params, uint32_t pack_uastc_flags) {
|
||||
params->m_pack_uastc_flags = pack_uastc_flags;
|
||||
}
|
||||
|
||||
void compressor_params_set_uastc(basisu::basis_compressor_params *params,
|
||||
bool is_uastc) {
|
||||
params->m_uastc = is_uastc;
|
||||
}
|
||||
|
||||
void compressor_params_set_perceptual(basisu::basis_compressor_params *params,
|
||||
bool perceptual) {
|
||||
params->m_perceptual = perceptual;
|
||||
}
|
||||
|
||||
void compressor_params_set_mip_srgb(basisu::basis_compressor_params *params,
|
||||
bool mip_srgb) {
|
||||
params->m_mip_srgb = mip_srgb;
|
||||
}
|
||||
|
||||
void compressor_params_set_no_selector_rdo(
|
||||
basisu::basis_compressor_params *params, bool no_selector_rdo) {
|
||||
params->m_no_selector_rdo = no_selector_rdo;
|
||||
}
|
||||
|
||||
void compressor_params_set_no_endpoint_rdo(
|
||||
basisu::basis_compressor_params *params, bool no_endpoint_rdo) {
|
||||
params->m_no_endpoint_rdo = no_endpoint_rdo;
|
||||
}
|
||||
|
||||
void compressor_params_set_rdo_uastc(basisu::basis_compressor_params *params,
|
||||
bool rdo_uastc) {
|
||||
params->m_rdo_uastc = rdo_uastc;
|
||||
}
|
||||
|
||||
void compressor_params_set_generate_mipmaps(
|
||||
basisu::basis_compressor_params *params, bool generate_mipmaps) {
|
||||
params->m_mip_gen = generate_mipmaps;
|
||||
}
|
||||
|
||||
void compressor_params_set_rdo_uastc_quality_scalar(
|
||||
basisu::basis_compressor_params *params, float rdo_uastc_quality_scalar) {
|
||||
params->m_rdo_uastc_quality_scalar = rdo_uastc_quality_scalar;
|
||||
}
|
||||
|
||||
void compressor_params_set_mip_smallest_dimension(
|
||||
basisu::basis_compressor_params *params, int mip_smallest_dimension) {
|
||||
params->m_mip_smallest_dimension = mip_smallest_dimension;
|
||||
}
|
||||
|
||||
basisu::image *compressor_params_get_or_create_source_image(
|
||||
basisu::basis_compressor_params *params, uint32_t index) {
|
||||
if (params->m_source_images.size() < index + 1) {
|
||||
params->m_source_images.resize(index + 1);
|
||||
}
|
||||
|
||||
return ¶ms->m_source_images[index];
|
||||
}
|
||||
|
||||
void compressor_params_resize_source_image_list(
|
||||
basisu::basis_compressor_params *params, size_t size) {
|
||||
params->m_source_images.resize(size);
|
||||
}
|
||||
|
||||
void compressor_params_clear_source_image_list(
|
||||
basisu::basis_compressor_params *params) {
|
||||
params->clear();
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
void compressor_image_fill(basisu::image *image, const uint8_t *pData,
|
||||
uint32_t width, uint32_t height, uint32_t comps) {
|
||||
image->init(pData, width, height, comps);
|
||||
}
|
||||
|
||||
void compressor_image_resize(basisu::image *image, uint32_t w, uint32_t h,
|
||||
uint32_t p) {
|
||||
image->resize(w, h, p);
|
||||
}
|
||||
|
||||
uint32_t compressor_image_get_width(basisu::image *image) {
|
||||
return image->get_width();
|
||||
}
|
||||
|
||||
uint32_t compressor_image_get_height(basisu::image *image) {
|
||||
return image->get_height();
|
||||
}
|
||||
|
||||
uint32_t compressor_image_get_pitch(basisu::image *image) {
|
||||
return image->get_pitch();
|
||||
}
|
||||
|
||||
uint32_t compressor_image_get_total_pixels(basisu::image *image) {
|
||||
return image->get_total_pixels();
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
basisu::basis_compressor *
|
||||
compressor_init(basisu::basis_compressor_params *params) {
|
||||
auto comp = new basisu::basis_compressor();
|
||||
if (comp->init(*params))
|
||||
return comp;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void compressor_deinit(basisu::basis_compressor *compressor) {
|
||||
delete compressor;
|
||||
}
|
||||
|
||||
basisu::basis_compressor::error_code
|
||||
compressor_process(basisu::basis_compressor *compressor) {
|
||||
return compressor->process();
|
||||
}
|
||||
|
||||
const uint8_t *compressor_get_output(basisu::basis_compressor *compressor) {
|
||||
return compressor->get_output_basis_file().data();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
compressor_get_output_size(const basisu::basis_compressor *compressor) {
|
||||
return compressor->get_basis_file_size();
|
||||
}
|
||||
|
||||
double compressor_get_output_bits_per_texel(
|
||||
const basisu::basis_compressor *compressor) {
|
||||
return compressor->get_basis_bits_per_texel();
|
||||
}
|
||||
|
||||
bool compressor_get_any_source_image_has_alpha(
|
||||
const basisu::basis_compressor *compressor) {
|
||||
return compressor->get_any_source_image_has_alpha();
|
||||
}
|
||||
}
|
||||
45
basisu/src/main.zig
Normal file
45
basisu/src/main.zig
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
pub usingnamespace @import("encoder.zig");
|
||||
pub usingnamespace @import("transcoder.zig");
|
||||
|
||||
pub const BasisTextureFormat = enum(u1) {
|
||||
etc1s = 0,
|
||||
uastc4x4 = 1,
|
||||
};
|
||||
|
||||
// Test
|
||||
|
||||
const t = @import("transcoder.zig");
|
||||
const e = @import("encoder.zig");
|
||||
const testing = @import("std").testing;
|
||||
|
||||
test "reference decls" {
|
||||
testing.refAllDeclsRecursive(t);
|
||||
testing.refAllDeclsRecursive(e);
|
||||
}
|
||||
|
||||
test "encode/transcode" {
|
||||
// Encode
|
||||
e.init_encoder();
|
||||
|
||||
const params = e.CompressorParams.init(1);
|
||||
params.setGenerateMipMaps(true);
|
||||
params.setBasisFormat(.uastc4x4);
|
||||
params.setPackUASTCFlags(.{ .fastest = true });
|
||||
defer params.deinit();
|
||||
|
||||
const image = params.getImageSource(0);
|
||||
image.fill(@embedFile("../test/ziggy.png"), 379, 316, 4);
|
||||
|
||||
const comp = try e.Compressor.init(params);
|
||||
try comp.process();
|
||||
|
||||
// Transcode
|
||||
t.init_transcoder();
|
||||
|
||||
const trans = try t.Transcoder.init(comp.output());
|
||||
defer trans.deinit();
|
||||
|
||||
var out_buf = try testing.allocator.alloc(u8, try trans.calcTranscodedSize(0, 0, .astc_4x4_rgba));
|
||||
defer testing.allocator.free(out_buf);
|
||||
try trans.transcode(out_buf, 0, 0, .astc_4x4_rgba, .{});
|
||||
}
|
||||
229
basisu/src/transcoder.zig
Normal file
229
basisu/src/transcoder.zig
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
const std = @import("std");
|
||||
const b = @import("transcoder/binding.zig");
|
||||
const BasisTextureFormat = @import("main.zig").BasisTextureFormat;
|
||||
const testing = std.testing;
|
||||
|
||||
/// Must be called before a `.basis` file can be transcoded.
|
||||
/// NOTE: this function *isn't* thread safe.
|
||||
pub fn init_transcoder() void {
|
||||
b.basisu_transcoder_init();
|
||||
}
|
||||
|
||||
/// Returns true if the specified format was enabled at compile time.
|
||||
pub fn isFormatEnabled(self: BasisTextureFormat, transcoder_format: Transcoder.TextureFormat) bool {
|
||||
return b.transcoder_is_format_supported(@enumToInt(self), @enumToInt(transcoder_format));
|
||||
}
|
||||
|
||||
pub const Transcoder = struct {
|
||||
handle: *b.BasisFile,
|
||||
|
||||
pub fn init(src: []const u8) error{Unknown}!Transcoder {
|
||||
const h = b.transcoder_init(src.ptr, @intCast(u32, src.len));
|
||||
return if (!b.transcoder_start_transcoding(h))
|
||||
error.Unknown
|
||||
else .{ .handle = h };
|
||||
}
|
||||
|
||||
pub fn deinit(self: Transcoder) void {
|
||||
if (!b.transcoder_stop_transcoding(self.handle))
|
||||
unreachable;
|
||||
b.transcoder_deinit(self.handle);
|
||||
}
|
||||
|
||||
/// Returns the total number of images in the basis file (always 1 or more).
|
||||
/// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
|
||||
pub fn getImageCount(self: Transcoder) u32 {
|
||||
return b.transcoder_get_images_count(self.handle);
|
||||
}
|
||||
|
||||
/// Returns the number of mipmap levels in an image.
|
||||
pub fn getImageLevelCount(self: Transcoder, image_index: u32) u32 {
|
||||
return b.transcoder_get_levels_count(self.handle, image_index);
|
||||
}
|
||||
|
||||
/// Returns basic information about an image.
|
||||
/// Note that orig_width/orig_height may not be a multiple of 4.
|
||||
pub fn getImageLevelDescriptor(self: Transcoder, image_index: u32, level_index: u32) error{OutOfBoundsLevelIndex}!ImageLevelDescriptor {
|
||||
var desc: ImageLevelDescriptor = undefined;
|
||||
return if (b.transcoder_get_image_level_desc(
|
||||
self.handle,
|
||||
image_index,
|
||||
level_index,
|
||||
&desc.original_width,
|
||||
&desc.original_height,
|
||||
&desc.block_count,
|
||||
))
|
||||
desc
|
||||
else
|
||||
error.OutOfBoundsLevelIndex;
|
||||
}
|
||||
|
||||
/// Returns the bytes neeeded to store output.
|
||||
pub fn calcTranscodedSize(self: Transcoder, image_index: u32, level_index: u32, format: TextureFormat) error{OutOfBoundsLevelIndex}!u32 {
|
||||
var size: u32 = undefined;
|
||||
return if (b.transcoder_get_image_transcoded_size(self.handle, image_index, level_index, @enumToInt(format), &size))
|
||||
size
|
||||
else
|
||||
error.OutOfBoundsLevelIndex;
|
||||
}
|
||||
|
||||
pub const TranscodeParams = struct {
|
||||
decode_flags: ?DecodeFlags = null,
|
||||
/// Output row pitch in blocks or pixels.
|
||||
/// Should be at least the image level's total_blocks (num_blocks_x * num_blocks_y),
|
||||
/// or the total number of output pixels if fmt==cTFRGBA32.
|
||||
output_row_pitch: ?u32 = null,
|
||||
/// Output rows in pixels
|
||||
/// Ignored unless fmt is uncompressed (cRGBA32, etc.).
|
||||
/// The total number of output rows in the output buffer. If 0,
|
||||
/// the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
|
||||
output_rows: ?u32 = null,
|
||||
};
|
||||
|
||||
/// Decodes a single mipmap level from the .basis file to any of the supported output texture formats.
|
||||
/// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2,
|
||||
/// due to PVRTC1 format requirements.
|
||||
/// NOTE:
|
||||
/// - `transcoder_init()` must have been called first to initialize
|
||||
/// the transcoder lookup tables before calling this function.
|
||||
/// - This method assumes the output texture buffer is readable.
|
||||
/// In some cases to handle alpha, the transcoder will write temporary data
|
||||
/// to the output texture in a first pass, which will be read in a second pass.
|
||||
pub fn transcode(
|
||||
self: Transcoder,
|
||||
out_buf: []u8,
|
||||
image_index: u32,
|
||||
level_index: u32,
|
||||
format: TextureFormat,
|
||||
params: TranscodeParams,
|
||||
) error{Unknown}!void {
|
||||
if (!b.transcoder_transcode(
|
||||
self.handle,
|
||||
out_buf.ptr,
|
||||
@intCast(u32, out_buf.len),
|
||||
image_index,
|
||||
level_index,
|
||||
@enumToInt(format),
|
||||
if (params.decode_flags) |f| f.cast() else 0,
|
||||
params.output_row_pitch orelse 0,
|
||||
params.output_rows orelse 0,
|
||||
)) return error.Unknown;
|
||||
}
|
||||
|
||||
pub const ImageLevelDescriptor = struct {
|
||||
original_width: u32,
|
||||
original_height: u32,
|
||||
block_count: u32,
|
||||
};
|
||||
|
||||
pub const DecodeFlags = packed struct {
|
||||
pvrtc_decode_to_next_pow_2: bool = false,
|
||||
transcode_alpha_data_to_opaque_formats: bool = false,
|
||||
bc1_forbid_three_color_blocks: bool = false,
|
||||
output_has_alpha_indices: bool = false,
|
||||
high_quality: bool = false,
|
||||
|
||||
pub const Flag = enum(u32) {
|
||||
pvrtc_decode_to_next_pow_2 = 2,
|
||||
transcode_alpha_data_to_opaque_formats = 4,
|
||||
bc1_forbid_three_color_blocks = 8,
|
||||
output_has_alpha_indices = 16,
|
||||
high_quality = 32,
|
||||
};
|
||||
|
||||
pub fn from(bits: u32) DecodeFlags {
|
||||
var value = DecodeFlags{};
|
||||
inline for (comptime std.meta.fieldNames(Flag)) |field_name| {
|
||||
if (bits & (@enumToInt(@field(Flag, field_name))) != 0) {
|
||||
@field(value, field_name) = true;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn cast(self: DecodeFlags) u32 {
|
||||
var value: u32 = 0;
|
||||
inline for (comptime std.meta.fieldNames(Flag)) |field_name| {
|
||||
if (@field(self, field_name)) {
|
||||
value |= @enumToInt(@field(Flag, field_name));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
pub const TextureFormat = enum(u5) {
|
||||
etc1_rgb = 0,
|
||||
etc2_rgba = 1,
|
||||
bc1_rgb = 2,
|
||||
bc3_rgba = 3,
|
||||
bc4_r = 4,
|
||||
bc5_rg = 5,
|
||||
bc7_rgba = 6,
|
||||
bc7_alt = 7,
|
||||
pvrtc1_4_rgb = 8,
|
||||
pvrtc1_4_rgba = 9,
|
||||
astc_4x4_rgba = 10,
|
||||
atc_rgb = 11,
|
||||
atc_rgba = 12,
|
||||
rgba32 = 13,
|
||||
rgb565 = 14,
|
||||
bgr565 = 15,
|
||||
rgba4444 = 16,
|
||||
fxt1_rgb = 17,
|
||||
pvrtc2_4_rgb = 18,
|
||||
pvrtc2_4_rgba = 19,
|
||||
etc2_eac_r11 = 20,
|
||||
etc2_eac_rg11 = 21,
|
||||
|
||||
pub fn isEnabled(
|
||||
self: TextureFormat,
|
||||
basis_texture_format: BasisTextureFormat,
|
||||
) bool {
|
||||
return isFormatEnabled(basis_texture_format, self);
|
||||
}
|
||||
|
||||
pub fn bytesPerBlockOrPixel(self: TextureFormat) u5 {
|
||||
return switch (self) {
|
||||
.rgb565, .bgr565, .rgba4444 => return 2,
|
||||
.rgba32 => return 4,
|
||||
.etc1_rgb, .bc1_rgb, .bc4_r, .pvrtc1_4_rgb, .pvrtc1_4_rgba, .atc_rgb, .pvrtc2_4_rgb, .pvrtc2_4_rgba, .etc2_eac_r11 => 8,
|
||||
.bc7_rgba, .bc7_alt, .etc2_rgba, .bc3_rgba, .bc5_rg, .astc_4x4_rgba, .atc_rgba, .fxt1_rgb, .etc2_eac_rg11 => return 16,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const BlockFormat = enum(u5) {
|
||||
etc1 = 0,
|
||||
etc2_rgba = 1,
|
||||
bc1 = 2,
|
||||
bc3 = 3,
|
||||
bc4 = 4,
|
||||
bc5 = 5,
|
||||
pvrtc1_4_rgb = 6,
|
||||
pvrtc1_4_rgba = 7,
|
||||
bc7 = 8,
|
||||
bc7_m5_color = 9,
|
||||
bc7_m5_alpha = 10,
|
||||
etc2_eac_a8 = 11,
|
||||
astc_4x4 = 12,
|
||||
atc_rgb = 13,
|
||||
atc_rgba_interpolated_alpha = 14,
|
||||
fxt1_rgb = 15,
|
||||
pvrtc2_4_rgb = 16,
|
||||
pvrtc2_4_rgba = 17,
|
||||
etc2_eac_r11 = 18,
|
||||
etc2_eac_rg11 = 19,
|
||||
indices = 20,
|
||||
rgb32 = 21,
|
||||
rgba32 = 23,
|
||||
a32 = 24,
|
||||
rgb565 = 25,
|
||||
bgr565 = 26,
|
||||
rgba4444_color = 27,
|
||||
rgba4444_alpha = 28,
|
||||
rgba4444_color_opaque = 29,
|
||||
rgba4444 = 30,
|
||||
uastc_4x4 = 31,
|
||||
};
|
||||
};
|
||||
38
basisu/src/transcoder/binding.zig
Normal file
38
basisu/src/transcoder/binding.zig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
pub const BasisFile = opaque {};
|
||||
|
||||
pub extern fn basisu_transcoder_init() void;
|
||||
|
||||
pub extern fn transcoder_is_format_supported(tex_type: u32, fmt: u32) bool;
|
||||
|
||||
pub extern fn transcoder_init([*]const u8, u32) *BasisFile;
|
||||
pub extern fn transcoder_deinit(*BasisFile) void;
|
||||
pub extern fn transcoder_get_images_count(*BasisFile) u32;
|
||||
pub extern fn transcoder_get_levels_count(*BasisFile, image_index: u32) u32;
|
||||
pub extern fn transcoder_get_image_level_desc(
|
||||
*BasisFile,
|
||||
image_index: u32,
|
||||
level_index: u32,
|
||||
orig_width: *u32,
|
||||
orig_height: *u32,
|
||||
total_block: *u32,
|
||||
) bool;
|
||||
pub extern fn transcoder_get_image_transcoded_size(
|
||||
*BasisFile,
|
||||
image_index: u32,
|
||||
level_index: u32,
|
||||
format: u32,
|
||||
size: *u32,
|
||||
) bool;
|
||||
pub extern fn transcoder_start_transcoding(*BasisFile) bool;
|
||||
pub extern fn transcoder_stop_transcoding(*BasisFile) bool;
|
||||
pub extern fn transcoder_transcode(
|
||||
*BasisFile,
|
||||
out: [*]const u8,
|
||||
out_size: u32,
|
||||
image_index: u32,
|
||||
level_index: u32,
|
||||
format: u32,
|
||||
decode_flags: u32,
|
||||
output_row_pitch_in_blocks_or_pixels: u32,
|
||||
output_rows_in_pixels: u32,
|
||||
) bool;
|
||||
153
basisu/src/transcoder/wrapper.cpp
Normal file
153
basisu/src/transcoder/wrapper.cpp
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#include <basisu_transcoder.h>
|
||||
#include <cstdbool>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
void basisu_transcoder_init();
|
||||
|
||||
bool transcoder_is_format_supported(uint32_t tex_type, uint32_t fmt);
|
||||
|
||||
void *transcoder_init(void *src, uint32_t src_size);
|
||||
void transcoder_deinit(void *h);
|
||||
|
||||
uint32_t transcoder_get_images_count(void *h);
|
||||
uint32_t transcoder_get_levels_count(void *h, uint32_t image_index);
|
||||
|
||||
bool transcoder_get_image_level_desc(void *h, uint32_t image_index,
|
||||
uint32_t level_index, uint32_t &orig_width,
|
||||
uint32_t &orig_height,
|
||||
uint32_t &block_count);
|
||||
|
||||
bool transcoder_get_image_transcoded_size(void *h, uint32_t image_index,
|
||||
uint32_t level_index, uint32_t format,
|
||||
uint32_t &size);
|
||||
|
||||
bool transcoder_start_transcoding(void *h);
|
||||
bool transcoder_stop_transcoding(void *h);
|
||||
|
||||
bool transcoder_transcode(void *h, void *out, uint32_t out_size,
|
||||
uint32_t image_index, uint32_t level_index,
|
||||
uint32_t format, uint32_t decode_flags,
|
||||
uint32_t output_row_pitch_in_blocks_or_pixels,
|
||||
uint32_t output_rows_in_pixels);
|
||||
}
|
||||
|
||||
void basisu_transcoder_init() { basist::basisu_transcoder_init(); }
|
||||
|
||||
#define MAGIC 0xDEADBEE1
|
||||
|
||||
struct basis_file {
|
||||
int m_magic;
|
||||
basist::basisu_transcoder m_transcoder;
|
||||
void *m_pFile;
|
||||
uint32_t m_file_size;
|
||||
|
||||
basis_file() : m_transcoder() {}
|
||||
};
|
||||
|
||||
// transcoder_texture_format, basis_tex_format
|
||||
bool transcoder_is_format_supported(uint32_t tex_type, uint32_t fmt) {
|
||||
return basist::basis_is_format_supported(
|
||||
(basist::transcoder_texture_format)tex_type,
|
||||
(basist::basis_tex_format)fmt);
|
||||
}
|
||||
|
||||
// !null - success
|
||||
// null - validation failure
|
||||
void *transcoder_init(void *src, uint32_t src_size) {
|
||||
auto f = new basis_file;
|
||||
f->m_pFile = src;
|
||||
f->m_file_size = src_size;
|
||||
|
||||
if (!f->m_transcoder.validate_header(f->m_pFile, f->m_file_size)) {
|
||||
delete f;
|
||||
return nullptr;
|
||||
}
|
||||
f->m_magic = MAGIC;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void transcoder_deinit(void *h) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
delete f;
|
||||
}
|
||||
|
||||
uint32_t transcoder_get_images_count(void *h) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
return f->m_transcoder.get_total_images(f->m_pFile, f->m_file_size);
|
||||
}
|
||||
|
||||
uint32_t transcoder_get_levels_count(void *h, uint32_t image_index) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
return f->m_transcoder.get_total_image_levels(f->m_pFile, f->m_file_size,
|
||||
image_index);
|
||||
}
|
||||
|
||||
// true - success
|
||||
// false - OutOfBoundsLevelIndex
|
||||
bool transcoder_get_image_level_desc(void *h, uint32_t image_index,
|
||||
uint32_t level_index, uint32_t &orig_width,
|
||||
uint32_t &orig_height,
|
||||
uint32_t &block_count) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
return f->m_transcoder.get_image_level_desc(
|
||||
f->m_pFile, f->m_file_size, image_index, level_index, orig_width,
|
||||
orig_height, block_count);
|
||||
}
|
||||
|
||||
// true - success
|
||||
// false - OutOfBoundsLevelIndex
|
||||
bool transcoder_get_image_transcoded_size(void *h, uint32_t image_index,
|
||||
uint32_t level_index, uint32_t format,
|
||||
uint32_t &size) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
uint32_t orig_width, orig_height, total_blocks;
|
||||
if (!f->m_transcoder.get_image_level_desc(
|
||||
f->m_pFile, f->m_file_size, image_index, level_index, orig_width,
|
||||
orig_height, total_blocks))
|
||||
return false;
|
||||
|
||||
uint8_t bytes_per_block_or_pixel = basis_get_bytes_per_block_or_pixel(
|
||||
(basist::transcoder_texture_format)format);
|
||||
if (basis_transcoder_format_is_uncompressed(
|
||||
(basist::transcoder_texture_format)format)) {
|
||||
size = orig_width * orig_height * bytes_per_block_or_pixel;
|
||||
} else {
|
||||
size = total_blocks * bytes_per_block_or_pixel;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// true - success
|
||||
// false - unknown
|
||||
bool transcoder_start_transcoding(void *h) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
return f->m_transcoder.start_transcoding(f->m_pFile, f->m_file_size);
|
||||
}
|
||||
|
||||
// true - success
|
||||
// false - unknown
|
||||
bool transcoder_stop_transcoding(void *h) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
return f->m_transcoder.stop_transcoding();
|
||||
}
|
||||
|
||||
// true - success
|
||||
// false - unknown
|
||||
bool transcoder_transcode(void *h, void *out, uint32_t out_size,
|
||||
uint32_t image_index, uint32_t level_index,
|
||||
uint32_t format, uint32_t decode_flags,
|
||||
uint32_t output_row_pitch_in_blocks_or_pixels,
|
||||
uint32_t output_rows_in_pixels) {
|
||||
auto f = static_cast<basis_file *>(h);
|
||||
uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(
|
||||
(basist::transcoder_texture_format)format);
|
||||
|
||||
return f->m_transcoder.transcode_image_level(
|
||||
f->m_pFile, f->m_file_size, image_index, level_index, out,
|
||||
out_size / bytes_per_block, (basist::transcoder_texture_format)format,
|
||||
decode_flags, output_row_pitch_in_blocks_or_pixels, nullptr,
|
||||
output_rows_in_pixels);
|
||||
}
|
||||
BIN
basisu/test/ziggy.png
Normal file
BIN
basisu/test/ziggy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
1
basisu/upstream
Submodule
1
basisu/upstream
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d55a3f9f06adf9cc8c31f58e894d947cc6026da5
|
||||
Loading…
Add table
Add a link
Reference in a new issue