Skip to content

Commit 82d8f75

Browse files
python: add bindings for new vtfpp XTF stuff
1 parent 343171f commit 82d8f75

File tree

4 files changed

+62
-7
lines changed

4 files changed

+62
-7
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ if(SOURCEPP_BUILD_PYTHON_WRAPPERS)
251251
"sourcepp._sourcepp_impl.vtfpp"
252252
"sourcepp._sourcepp_impl.vtfpp.ImageFormatDetails"
253253
"sourcepp._sourcepp_impl.vtfpp.ImageDimensions"
254-
"sourcepp._sourcepp_impl.vtfpp.ImageConversion")
254+
"sourcepp._sourcepp_impl.vtfpp.ImageConversion"
255+
"sourcepp._sourcepp_impl.vtfpp.ImageQuantize")
255256
foreach(MODULE ${${SOURCEPP_PYTHON_NAME}_MODULES})
256257
string(REPLACE "." "/" MODULE_DIR "${MODULE}")
257258
string(REPLACE "." "_" MODULE_NAME_NORMALIZED "${MODULE}")

FUTURE.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- There is literally nothing else that supports BC6H
1111
- `bsppp`
1212
- Console paklump support
13-
- Note that XBOX uses PC format. X360, PS3 do not
13+
- Note that XBOX uses XZP lump, X360, PS3 use PC lump but console style zip
1414
- Support parsing more lumps
1515
- Think about GoldSrc support
1616
- `dmxpp`
@@ -42,6 +42,3 @@
4242
- XZP write support
4343
- `vtfpp`
4444
- Add a MKS parser/writer
45-
- Add DPID resize filter
46-
- Allow directly applying an HDRI to a VTF without requiring the user to write a custom wrapper
47-
- XTF support

include/vtfpp/vtfpp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "HOT.h"
99
#include "ImageConversion.h"
1010
#include "ImageFormats.h"
11+
#include "ImageQuantize.h"
1112
#include "PPL.h"
1213
#include "SHT.h"
1314
#include "TTX.h"

lang/python/src/vtfpp.h

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ inline void register_python(py::module_& m) {
120120

121121
ImageFormatDetails
122122
.def("get_data_length", py::overload_cast<ImageFormat, uint16_t, uint16_t, uint16_t>(&getDataLength), py::arg("format"), py::arg("width"), py::arg("height"), py::arg("slice_count") = 1)
123-
.def("get_data_length_extended", py::overload_cast<ImageFormat, uint8_t, uint16_t, uint8_t, uint16_t, uint16_t, uint16_t>(&getDataLength), py::arg("format"), py::arg("mip_count"), py::arg("frame_count"), py::arg("face_count"), py::arg("width"), py::arg("height"), py::arg("slice_count"));
123+
.def("get_data_length_extended", py::overload_cast<ImageFormat, uint8_t, uint16_t, uint8_t, uint16_t, uint16_t, uint16_t>(&getDataLength), py::arg("format"), py::arg("mip_count"), py::arg("frame_count"), py::arg("face_count"), py::arg("width"), py::arg("height"), py::arg("slice_count") = 1)
124+
.def("get_data_length_xbox", &getDataLengthXBOX, py::arg("padded"), py::arg("format"), py::arg("mip_count"), py::arg("frame_count"), py::arg("face_count"), py::arg("width"), py::arg("height"), py::arg("slice_count") = 1);
124125

125126
ImageFormatDetails.def("get_data_position", [](ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice = 0, uint16_t sliceCount = 1) -> std::pair<uint32_t, uint32_t> {
126127
uint32_t offset, length;
@@ -129,6 +130,14 @@ inline void register_python(py::module_& m) {
129130
}
130131
return {0, 0};
131132
}, py::arg("format"), py::arg("mip"), py::arg("mip_count"), py::arg("frame"), py::arg("frame_count"), py::arg("face"), py::arg("face_count"), py::arg("width"), py::arg("height"), py::arg("slice") = 0, py::arg("slice_count") = 1);
133+
134+
ImageFormatDetails.def("get_data_position_xbox", [](bool padded, ImageFormat format, uint8_t mip, uint8_t mipCount, uint16_t frame, uint16_t frameCount, uint8_t face, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t slice = 0, uint16_t sliceCount = 1) -> std::pair<uint32_t, uint32_t> {
135+
uint32_t offset, length;
136+
if (getDataPositionXbox(offset, length, padded, format, mip, mipCount, frame, frameCount, face, faceCount, width, height, slice, sliceCount)) {
137+
return {offset, length};
138+
}
139+
return {0, 0};
140+
}, py::arg("padded"), py::arg("format"), py::arg("mip"), py::arg("mip_count"), py::arg("frame"), py::arg("frame_count"), py::arg("face"), py::arg("face_count"), py::arg("width"), py::arg("height"), py::arg("slice") = 0, py::arg("slice_count") = 1);
132141
}
133142

134143
{
@@ -250,6 +259,16 @@ inline void register_python(py::module_& m) {
250259
// Skip extractChannelFromImageData, difficult to bind
251260
}
252261

262+
{
263+
using namespace ImageQuantize;
264+
auto ImageQuantize = vtfpp.def_submodule("ImageQuantize");
265+
266+
ImageQuantize.def("convert_p8_image_data_to_bgra8888", [](const py::bytes& paletteData, const py::bytes& imageData) {
267+
const auto d = convertP8ImageDataToBGRA8888({static_cast<const std::byte*>(paletteData.data()), paletteData.size()}, {static_cast<const std::byte*>(imageData.data()), imageData.size()});
268+
return py::bytes{d.data(), d.size()};
269+
}, py::arg("palette_data"), py::arg("image_data"));
270+
}
271+
253272
auto cPPL = py::class_<PPL>(vtfpp, "PPL");
254273

255274
py::class_<PPL::Image>(cPPL, "Image")
@@ -366,6 +385,9 @@ inline void register_python(py::module_& m) {
366385
.def("bake_to_file", py::overload_cast<const std::string&, const std::string&>(&TTX::bake, py::const_), py::arg("tth_path"), py::arg("ttz_path"));
367386

368387
vtfpp.attr("VTF_SIGNATURE") = VTF_SIGNATURE;
388+
vtfpp.attr("XTF_SIGNATURE") = XTF_SIGNATURE;
389+
vtfpp.attr("VTFX_SIGNATURE") = VTFX_SIGNATURE;
390+
vtfpp.attr("VTF3_SIGNATURE") = VTF3_SIGNATURE;
369391

370392
py::enum_<CompressionMethod>(vtfpp, "CompressionMethod", py::is_arithmetic())
371393
.value("DEFLATE", CompressionMethod::DEFLATE)
@@ -378,6 +400,8 @@ inline void register_python(py::module_& m) {
378400
py::enum_<Resource::Type>(cResource, "Type")
379401
.value("UNKNOWN", Resource::TYPE_UNKNOWN)
380402
.value("THUMBNAIL_DATA", Resource::TYPE_THUMBNAIL_DATA)
403+
.value("PALETTE_DATA", Resource::TYPE_PALETTE_DATA)
404+
.value("FALLBACK_DATA", Resource::TYPE_FALLBACK_DATA)
381405
.value("PARTICLE_SHEET_DATA", Resource::TYPE_PARTICLE_SHEET_DATA)
382406
.value("HOTSPOT_DATA", Resource::TYPE_HOTSPOT_DATA)
383407
.value("IMAGE_DATA", Resource::TYPE_IMAGE_DATA)
@@ -433,6 +457,11 @@ inline void register_python(py::module_& m) {
433457
.value("CLAMP_U", VTF::FLAG_V2_CLAMP_U)
434458
.export_values();
435459

460+
py::enum_<VTF::FlagsXBOX>(cVTF, "FlagsXBOX", py::is_flag())
461+
.value("CACHEABLE", VTF::FLAG_XBOX_CACHEABLE)
462+
.value("UNFILTERABLE_OK", VTF::FLAG_XBOX_UNFILTERABLE_OK)
463+
.export_values();
464+
436465
py::enum_<VTF::FlagsV3>(cVTF, "FlagsV3", py::is_flag())
437466
.value("LOAD_ALL_MIPS", VTF::FLAG_V3_LOAD_ALL_MIPS)
438467
.value("VERTEX_TEXTURE", VTF::FLAG_V3_VERTEX_TEXTURE)
@@ -470,6 +499,7 @@ inline void register_python(py::module_& m) {
470499
py::enum_<VTF::Platform>(cVTF, "Platform")
471500
.value("UNKNOWN", VTF::PLATFORM_UNKNOWN)
472501
.value("PC", VTF::PLATFORM_PC)
502+
.value("XBOX", VTF::PLATFORM_XBOX)
473503
.value("X360", VTF::PLATFORM_X360)
474504
.value("PS3_ORANGEBOX", VTF::PLATFORM_PS3_ORANGEBOX)
475505
.value("PS3_PORTAL2", VTF::PLATFORM_PS3_PORTAL2)
@@ -495,7 +525,8 @@ inline void register_python(py::module_& m) {
495525
.def_rw("compression_method", &VTF::CreationOptions::compressionMethod)
496526
.def_rw("bumpmap_scale", &VTF::CreationOptions::bumpMapScale)
497527
.def_rw("gamma_correction", &VTF::CreationOptions::gammaCorrection)
498-
.def_rw("invert_green_channel", &VTF::CreationOptions::invertGreenChannel);
528+
.def_rw("invert_green_channel", &VTF::CreationOptions::invertGreenChannel)
529+
.def_rw("xbox_mip_scale", &VTF::CreationOptions::xboxMipScale);
499530

500531
cVTF
501532
.def_ro_static("FLAGS_MASK_V0", &VTF::FLAGS_MASK_V0)
@@ -557,6 +588,9 @@ inline void register_python(py::module_& m) {
557588
.def_prop_ro("thumbnail_format", &VTF::getThumbnailFormat)
558589
.def_prop_ro("thumbnail_width", &VTF::getThumbnailWidth)
559590
.def_prop_ro("thumbnail_height", &VTF::getThumbnailHeight)
591+
.def_prop_ro("fallback_width", &VTF::getFallbackWidth)
592+
.def_prop_ro("fallback_height", &VTF::getFallbackHeight)
593+
.def_prop_ro("fallback_mip_count", &VTF::getFallbackMipCount)
560594
// Skipping getResources, don't want to do the same hack as in SHT here, it's way more expensive
561595
.def("get_resource", &VTF::getResource, py::arg("type"), py::rv_policy::reference_internal)
562596
.def("get_particle_sheet_frame_data_raw", [](const VTF& self, uint32_t shtSequenceID, uint32_t shtFrame, uint8_t shtBounds = 0, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, uint16_t slice = 0) -> std::tuple<uint16_t, uint16_t, py::bytes> {
@@ -626,13 +660,35 @@ inline void register_python(py::module_& m) {
626660
.def("set_thumbnail", [](VTF& self, const py::bytes& imageData, ImageFormat format, uint16_t width, uint16_t height, float quality = ImageConversion::DEFAULT_COMPRESSED_QUALITY) {
627661
return self.setThumbnail({static_cast<const std::byte*>(imageData.data()), imageData.size()}, format, width, height, quality);
628662
}, py::arg("image_data"), py::arg("format"), py::arg("width"), py::arg("height"), py::arg("quality") = ImageConversion::DEFAULT_COMPRESSED_QUALITY)
663+
.def("set_thumbnail_from_file", py::overload_cast<const std::string&, float>(&VTF::setThumbnail), py::arg("image_path"), py::arg("quality") = ImageConversion::DEFAULT_COMPRESSED_QUALITY)
629664
.def("compute_thumbnail", &VTF::computeThumbnail, py::arg("filter") = ImageConversion::ResizeFilter::DEFAULT, py::arg("quality") = ImageConversion::DEFAULT_COMPRESSED_QUALITY)
630665
.def("remove_thumbnail", &VTF::removeThumbnail)
631666
.def("save_thumbnail", [](const VTF& self, ImageConversion::FileFormat fileFormat = ImageConversion::FileFormat::DEFAULT) {
632667
const auto d = self.saveThumbnailToFile(fileFormat);
633668
return py::bytes{d.data(), d.size()};
634669
}, py::arg("file_format") = ImageConversion::FileFormat::DEFAULT)
635670
.def("save_thumbnail_to_file", py::overload_cast<const std::string&, ImageConversion::FileFormat>(&VTF::saveThumbnailToFile, py::const_), py::arg("image_path"), py::arg("file_format") = ImageConversion::FileFormat::DEFAULT)
671+
.def("has_fallback_data", &VTF::hasFallbackData)
672+
.def("get_fallback_data_raw", [](const VTF& self, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0) {
673+
const auto d = self.getFallbackDataRaw(mip, frame, face);
674+
return py::bytes{d.data(), d.size()};
675+
}, py::arg("mip") = 0, py::arg("frame") = 0, py::arg("face") = 0)
676+
.def("get_fallback_data_as", [](const VTF& self, ImageFormat newFormat, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0) {
677+
const auto d = self.getFallbackDataAs(newFormat, mip, frame, face);
678+
return py::bytes{d.data(), d.size()};
679+
}, py::arg("new_format"), py::arg("mip") = 0, py::arg("frame") = 0, py::arg("face") = 0)
680+
.def("get_fallback_data_as_rgba8888", [](const VTF& self, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0) {
681+
const auto d = self.getFallbackDataAsRGBA8888(mip, frame, face);
682+
return py::bytes{d.data(), d.size()};
683+
}, py::arg("mip") = 0, py::arg("frame") = 0, py::arg("face") = 0)
684+
.def("compute_fallback", &VTF::computeFallback, py::arg("filter") = ImageConversion::ResizeFilter::DEFAULT)
685+
.def("remove_fallback", &VTF::removeFallback)
686+
.def("save_fallback", [](const VTF& self, uint8_t mip = 0, uint16_t frame = 0, uint8_t face = 0, ImageConversion::FileFormat fileFormat = ImageConversion::FileFormat::DEFAULT) {
687+
const auto d = self.saveFallbackToFile(mip, frame, face, fileFormat);
688+
return py::bytes{d.data(), d.size()};
689+
}, py::arg("mip") = 0, py::arg("frame") = 0, py::arg("face") = 0, py::arg("file_format") = ImageConversion::FileFormat::DEFAULT)
690+
.def("save_fallback_to_file", py::overload_cast<const std::string&, uint8_t, uint16_t, uint8_t, ImageConversion::FileFormat>(&VTF::saveFallbackToFile, py::const_), py::arg("image_path"), py::arg("mip") = 0, py::arg("frame") = 0, py::arg("face") = 0, py::arg("file_format") = ImageConversion::FileFormat::DEFAULT)
691+
.def_prop_rw("xbox_mip_scale", &VTF::getXBOXMipScale, &VTF::setXBOXMipScale)
636692
.def("bake", [](const VTF& self) {
637693
const auto d = self.bake();
638694
return py::bytes{d.data(), d.size()};

0 commit comments

Comments
 (0)