5#include <unordered_map>
8#ifdef SOURCEPP_BUILD_WITH_TBB
12#ifdef SOURCEPP_BUILD_WITH_THREADS
17#include <BufferStream.h>
30[[nodiscard]] std::vector<std::byte> compressData(std::span<const std::byte> data, int16_t level,
CompressionMethod method) {
34 mz_ulong compressedSize = mz_compressBound(data.size());
35 std::vector<std::byte> out(compressedSize);
38 while ((status = mz_compress2(
reinterpret_cast<unsigned char*
>(out.data()), &compressedSize,
reinterpret_cast<const unsigned char*
>(data.data()), data.size(), level)) == MZ_BUF_ERROR) {
40 out.resize(compressedSize);
43 if (status != MZ_OK) {
46 out.resize(compressedSize);
54 const auto expectedSize = ZSTD_compressBound(data.size());
55 std::vector<std::byte> out(expectedSize);
57 const auto compressedSize = ZSTD_compress(out.data(), expectedSize, data.data(), data.size(), level);
58 if (ZSTD_isError(compressedSize)) {
62 out.resize(compressedSize);
75template<std::
unsigned_
integral T,
bool ExistingDataIsSwizzled>
76constexpr void swizzleUncompressedImageData(std::span<std::byte> inputData, std::span<std::byte> outputData,
ImageFormat format, uint16_t width, uint16_t height, uint16_t depth) {
82 ](uint32_t x, uint32_t y, uint32_t z) {
83 auto widthL2m = widthL2;
84 auto heightL2m = heightL2;
85 auto depthL2m = depthL2;
87 uint32_t shiftCount = 0;
90 offset |= (z & 1) << shiftCount++;
93 if (heightL2m --> 0) {
94 offset |= (y & 1) << shiftCount++;
98 offset |= (x & 1) << shiftCount++;
105 const auto* inputPtr =
reinterpret_cast<const T*
>(inputData.data());
106 auto* outputPtr =
reinterpret_cast<T*
>(outputData.data());
107 for (uint16_t x = 0; x < width; x++) {
108 for (uint16_t y = 0; y < height; y++) {
109 for (uint16_t z = 0; z < depth; z++) {
110 if constexpr (ExistingDataIsSwizzled) {
111 *outputPtr++ =
reinterpret_cast<const T*
>(inputData.data())[zIndex(x, y, z)];
113 reinterpret_cast<T*
>(outputData.data())[zIndex(x, y, z)] = *inputPtr++;
120template<
bool ConvertingFromSource>
121void swapImageDataEndianForConsole(std::span<std::byte> imageData,
ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth,
VTF::Platform platform) {
122 if (imageData.empty() || format == ImageFormat::EMPTY || platform ==
VTF::PLATFORM_PC) {
134 std::ranges::copy(newData, imageData.begin());
142 std::span dxtData{
reinterpret_cast<uint16_t*
>(imageData.data()), imageData.size() /
sizeof(uint16_t)};
144#ifdef SOURCEPP_BUILD_WITH_TBB
145 std::execution::par_unseq,
147 dxtData.begin(), dxtData.end(), [](uint16_t& value) {
148 BufferStream::swap_endian(&value);
159 std::vector<std::byte> out(imageData.size());
160 for(
int mip = mipCount - 1; mip >= 0; mip--) {
163 for (
int frame = 0; frame < frameCount; frame++) {
164 for (
int face = 0; face < faceCount; face++) {
165 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, format, mip, mipCount, frame, frameCount, face, faceCount, width, height)) {
166 std::span imageDataSpan{imageData.data() + offset, length * depth};
167 std::span outSpan{out.data() + offset, length * depth};
169 ::swizzleUncompressedImageData<uint32_t, ConvertingFromSource>(imageDataSpan, outSpan, format, mipWidth, mipHeight, depth);
171 ::swizzleUncompressedImageData<uint16_t, ConvertingFromSource>(imageDataSpan, outSpan, format, mipWidth, mipHeight, depth);
173 ::swizzleUncompressedImageData<uint8_t, ConvertingFromSource>(imageDataSpan, outSpan, format, mipWidth, mipHeight, depth);
179 std::memcpy(imageData.data(), out.data(), out.size());
183template<
bool ConvertingFromDDS>
184[[nodiscard]] std::vector<std::byte> convertBetweenDDSAndVTFMipOrderForXBOX(
bool padded, std::span<const std::byte> imageData,
ImageFormat format, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth,
bool& ok) {
185 std::vector<std::byte> reorderedImageData;
187 BufferStream reorderedStream{reorderedImageData};
189 if constexpr (ConvertingFromDDS) {
190 for (
int i = mipCount - 1; i >= 0; i--) {
191 for (
int j = 0; j < frameCount; j++) {
192 for (
int k = 0; k < faceCount; k++) {
193 for (
int l = 0; l < depth; l++) {
194 uint32_t oldOffset, length;
195 if (!
ImageFormatDetails::getDataPositionXbox(oldOffset, length, padded, format, i, mipCount, j, frameCount, k, faceCount, width, height, l, depth)) {
199 reorderedStream << imageData.subspan(oldOffset, length);
205 for (
int j = 0; j < frameCount; j++) {
206 for (
int k = 0; k < faceCount; k++) {
207 for (
int i = 0; i < mipCount; i++) {
208 for (
int l = 0; l < depth; l++) {
209 uint32_t oldOffset, length;
210 if (!
ImageFormatDetails::getDataPosition(oldOffset, length, format, i, mipCount, j, frameCount, k, faceCount, width, height, l, depth)) {
214 reorderedStream << imageData.subspan(oldOffset, length);
218 if (padded && j + 1 != frameCount && reorderedStream.tell() > 512) {
225 return reorderedImageData;
231 switch (this->
type) {
233 if (this->data.size() <=
sizeof(uint32_t)) {
236 return SHT{{
reinterpret_cast<const std::byte*
>(this->data.data()) +
sizeof(uint32_t), *
reinterpret_cast<const uint32_t*
>(this->data.data())}};
239 if (this->data.size() !=
sizeof(uint32_t)) {
242 return *
reinterpret_cast<const uint32_t*
>(this->data.data());
244 if (this->data.size() !=
sizeof(uint32_t)) {
247 return std::make_tuple(
248 *(
reinterpret_cast<const uint8_t*
>(this->data.data()) + 0),
249 *(
reinterpret_cast<const uint8_t*
>(this->data.data()) + 1),
250 *(
reinterpret_cast<const uint8_t*
>(this->data.data()) + 2),
251 *(
reinterpret_cast<const uint8_t*
>(this->data.data()) + 3));
253 if (this->data.size() <=
sizeof(uint32_t)) {
256 return std::string(
reinterpret_cast<const char*
>(this->data.data()) +
sizeof(uint32_t), *
reinterpret_cast<const uint32_t*
>(this->data.data()));
258 if (this->data.size() <=
sizeof(uint32_t)) {
261 return HOT{{
reinterpret_cast<const std::byte*
>(this->data.data()) +
sizeof(uint32_t), *
reinterpret_cast<const uint32_t*
>(this->data.data())}};
272VTF::VTF(std::vector<std::byte>&& vtfData,
bool parseHeaderOnly)
273 : data(std::move(vtfData)) {
274 BufferStreamReadOnly stream{this->data};
276 if (
const auto signature = stream.read<uint32_t>(); signature ==
VTF_SIGNATURE) {
282 if (this->version > 6) {
286 stream.set_big_endian(
true);
292 if (this->version != 8) {
308 if (this->version != 0) {
317 const auto headerSize = stream.read<uint32_t>();
319 const auto readResources = [
this, &stream](uint32_t resourceCount) {
322 for (
int i = 0; i < resourceCount; i++) {
323 auto& [type, flags_, data_] = this->
resources.emplace_back();
325 auto typeAndFlags = stream.read<uint32_t>();
326 if (stream.is_big_endian()) {
328 BufferStream::swap_endian(&typeAndFlags);
332 data_ = stream.read_span<std::byte>(4);
335 BufferStream::swap_endian(
reinterpret_cast<uint32_t*
>(data_.data()));
344 return lhs.type < rhs.type;
352 return *
reinterpret_cast<uint32_t*
>(lhs.
data.data()) < *
reinterpret_cast<uint32_t*
>(rhs.
data.data());
360 const auto lastOffset = *
reinterpret_cast<uint32_t*
>(lastResource->data.data());
361 const auto currentOffset = *
reinterpret_cast<uint32_t*
>(resource.data.data());
362 const auto curPos = stream.tell();
363 stream.seek(lastOffset);
364 lastResource->
data = stream.read_span<std::byte>(currentOffset - lastOffset);
365 stream.seek(
static_cast<int64_t
>(curPos));
367 lastResource = &resource;
371 const auto offset = *
reinterpret_cast<uint32_t*
>(lastResource->data.data());
372 const auto curPos = stream.tell();
374 lastResource->data = stream.read_span<std::byte>(stream.size() - offset);
375 stream.seek(
static_cast<int64_t
>(curPos));
380 const auto postReadTransform = [
this] {
394 switch (this->platform) {
402 .read(this->frameCount)
411 .read(this->mipCount);
427 stream.read(this->depth);
430 if (parseHeaderOnly) {
437 auto resourceCount = stream.read<uint32_t>();
439 readResources(resourceCount);
441 this->
opened = stream.tell() == headerSize;
446 if (auxResource && imageResource) {
447 if (auxResource->getDataAsAuxCompressionLevel() != 0) {
449 std::vector<std::byte> decompressedImageData(
ImageFormatDetails::getDataLength(this->format, this->mipCount, this->frameCount, faceCount, this->width, this->height, this->depth));
450 uint32_t oldOffset = 0;
451 for (
int i = this->mipCount - 1; i >= 0; i--) {
453 for (
int k = 0; k < faceCount; k++) {
454 uint32_t oldLength = auxResource->getDataAsAuxCompressionLength(i, this->mipCount, j, this->frameCount, k, faceCount);
455 if (uint32_t newOffset, newLength;
ImageFormatDetails::getDataPosition(newOffset, newLength, this->format, i, this->mipCount, j, this->frameCount, k, faceCount, this->width, this->height, 0, this->
getDepth())) {
457 mz_ulong decompressedImageDataSize = newLength * this->
depth;
458 switch (auxResource->getDataAsAuxCompressionMethod()) {
461 if (mz_uncompress(
reinterpret_cast<unsigned char*
>(decompressedImageData.data() + newOffset), &decompressedImageDataSize,
reinterpret_cast<const unsigned char*
>(imageResource->data.data() + oldOffset), oldLength) != MZ_OK) {
467 if (
auto decompressedSize = ZSTD_decompress(
reinterpret_cast<unsigned char*
>(decompressedImageData.data() + newOffset), decompressedImageDataSize,
reinterpret_cast<const unsigned char*
>(imageResource->data.data() + oldOffset), oldLength); ZSTD_isError(decompressedSize) || decompressedSize != decompressedImageDataSize) {
478 oldOffset += oldLength;
488 this->
opened = stream.tell() == headerSize;
503 .data = stream.read_span<std::byte>(stream.size() - stream.tell()),
517 uint16_t preloadSize = 0, imageOffset = 0;
523 .read(this->frameCount)
538 const bool headerSizeIsAccurate = stream.tell() == headerSize;
569 std::vector<std::byte> reorderedFallbackData;
571 if (stream.tell() + fallbackSize != preloadSize) {
574 if (stream.tell() + fallbackSize != preloadSize) {
582 reorderedFallbackData = ::convertBetweenDDSAndVTFMipOrderForXBOX<true>(
false, stream.read_span<std::byte>(fallbackSize), this->format, this->fallbackMipCount, this->frameCount, faceCount, this->fallbackWidth, this->fallbackHeight, 1, ok);
590 this->
opened = headerSizeIsAccurate;
591 if (parseHeaderOnly) {
596 std::vector<std::byte> reorderedImageData;
598 reorderedImageData = ::convertBetweenDDSAndVTFMipOrderForXBOX<true>(
true, stream.seek(imageOffset).read_span<std::byte>(imageSize), this->format, this->mipCount, this->frameCount, faceCount, this->width, this->height, this->depth, ok);
603 ::swapImageDataEndianForConsole<true>(reorderedImageData, this->format, this->mipCount, this->frameCount, faceCount, this->width, this->height, this->depth, this->platform);
619 uint8_t resourceCount;
625 .read(this->frameCount)
634 .skip<math::Vec4ui8>()
641 stream.skip<uint32_t>();
646 if (parseHeaderOnly) {
652 readResources(resourceCount);
654 this->
opened = stream.tell() == headerSize;
657 for (
const auto& resource : this->
resources) {
671 ::swapImageDataEndianForConsole<true>(resource.data, this->thumbnailFormat, 1, 1, 1, this->thumbnailWidth, this->thumbnailHeight, 1, this->platform);
673 ::swapImageDataEndianForConsole<true>(resource.data, this->format, this->mipCount, this->frameCount, this->getFaceCount(), this->width, this->height, this->depth, this->platform);
675 BufferStream::swap_endian(
reinterpret_cast<uint32_t*
>(resource.data.data()));
683VTF::VTF(std::span<const std::byte> vtfData,
bool parseHeaderOnly)
684 :
VTF(std::vector<std::byte>{vtfData.begin(), vtfData.end()}, parseHeaderOnly) {}
686VTF::VTF(
const std::string& vtfPath,
bool parseHeaderOnly)
687 :
VTF(fs::readFileBuffer(vtfPath), parseHeaderOnly) {}
695 this->data = other.
data;
697 this->width = other.
width;
698 this->height = other.
height;
704 this->format = other.
format;
713 this->depth = other.
depth;
716 for (
const auto& [otherType, otherFlags, otherData] : other.
resources) {
717 auto& [type, flags_, data_] = this->
resources.emplace_back();
720 data_ = {this->data.data() + (otherData.data() - other.
data.data()), otherData.size()};
732VTF::operator bool()
const {
739 for (
int i = 1; i < writer.
mipCount; i++) {
742 for (
int l = 0; l < writer.
depth; l++) {
743 if (options.
invertGreenChannel && !writer.
setImage(
ImageConversion::invertGreenChannelForImageData(writer.
getImageDataRaw(i, j, k, l), writer.
getFormat(), writer.
getWidth(i), writer.
getHeight(i)), writer.
getFormat(), writer.
getWidth(i), writer.
getHeight(i),
ImageConversion::ResizeFilter::DEFAULT, i, j, k, l)) {
746 if (options.
gammaCorrection != 1.f && !writer.
setImage(
ImageConversion::gammaCorrectImageData(writer.
getImageDataRaw(i, j, k, l), writer.
getFormat(), writer.
getWidth(i), writer.
getHeight(i), options.
gammaCorrection), writer.
getFormat(), writer.
getWidth(i), writer.
getHeight(i),
ImageConversion::ResizeFilter::DEFAULT, i, j, k, l)) {
802 return writer.
bake(vtfPath);
806 std::vector<std::byte> imageData;
822 std::vector<std::byte> imageData;
838 return writer.
bake(vtfPath);
856 if (this->platform == newPlatform) {
861 const auto oldPlatform = this->
platform;
863 switch (newPlatform) {
878 this->platform = newPlatform;
891 std::array newThumbnail{
892 static_cast<std::byte
>(
static_cast<uint8_t
>(std::clamp(this->
reflectivity[0], 0.f, 1.f) * 255.f)),
893 static_cast<std::byte
>(
static_cast<uint8_t
>(std::clamp(this->
reflectivity[1], 0.f, 1.f) * 255.f)),
894 static_cast<std::byte
>(
static_cast<uint8_t
>(std::clamp(this->
reflectivity[2], 0.f, 1.f) * 255.f)),
915 if (this->mipCount != recommendedCount) {
920 if (this->mipCount > recommendedMipCount) {
936 if (faceCount == 7 && (newVersion < 1 || newVersion > 4)) {
939 this->
regenerateImageData(this->format, this->width, this->height, this->mipCount, this->frameCount, faceCount, this->depth);
943 const bool srgb = this->
isSRGB();
944 if ((this->version < 1 && newVersion >= 1) || (this->
version >= 1 && newVersion < 1)) {
947 if ((this->version < 2 && newVersion >= 2) || (this->
version >= 2 && newVersion < 2)) {
950 if ((this->version < 3 && newVersion >= 3) || (this->
version >= 3 && newVersion < 3)) {
953 if ((this->version < 4 && newVersion >= 4) || (this->
version >= 4 && newVersion < 4)) {
957 if ((this->version < 5 && newVersion >= 5) || (this->
version >= 5 && newVersion < 5)) {
996 if (newWidth == 0 || newHeight == 0) {
1006 if (this->width == newWidth && this->height == newHeight) {
1012 newMipCount = recommendedCount;
1021 this->frameCount = 1;
1022 this->
flags &= ~FLAG_V0_ENVMAP;
1023 this->width = newWidth;
1024 this->height = newHeight;
1039 this->
flags |= flags_ & ~FLAG_MASK_INTERNAL;
1054 }
else if (this->
version >= 4) {
1060 }
else if (this->
version >= 4) {
1069 this->
flags &= ~FLAG_V0_ONE_BIT_ALPHA;
1073 this->
flags &= ~FLAG_V0_MULTI_BIT_ALPHA;
1076 this->
flags &= ~FLAG_V0_ONE_BIT_ALPHA;
1077 this->
flags &= ~FLAG_V0_MULTI_BIT_ALPHA;
1106 this->format = newFormat;
1109 const auto oldFormat = this->
format;
1112 newMipCount = recommendedCount;
1115 this->
regenerateImageData(newFormat, this->width +
math::paddingForAlignment(4, this->width), this->height +
math::paddingForAlignment(4, this->height), newMipCount, this->frameCount, this->
getFaceCount(), this->depth, filter, quality);
1121 const auto fallbackConverted =
ImageConversion::convertSeveralImageDataToFormat(fallbackResource->data, oldFormat, this->format,
ImageDimensions::getActualMipCountForDimsOnConsole(this->fallbackWidth, this->fallbackHeight), this->frameCount, this->getFaceCount(), this->fallbackWidth, this->fallbackHeight, 1, quality);
1134 if (this->platform !=
PLATFORM_PC && newMipCount > 1) {
1137 newMipCount = recommended;
1138 if (newMipCount == 1) {
1159 if (this->mipCount <= 1) {
1165 auto* outputDataPtr = imageResource->data.data();
1168#ifdef SOURCEPP_BUILD_WITH_THREADS
1169 std::vector<std::future<void>> futures;
1170 futures.reserve(this->frameCount * faceCount * this->depth);
1173 for (
int k = 0; k < faceCount; k++) {
1174 for (
int l = 0; l < this->
depth; l++) {
1175#ifdef SOURCEPP_BUILD_WITH_THREADS
1176 futures.push_back(std::async(std::launch::async, [
this, filter, outputDataPtr, faceCount, j, k, l] {
1178 for (
int i = 1; i < this->
mipCount; i++) {
1179 auto mip =
ImageConversion::resizeImageData(this->
getImageDataRaw(i - 1, j, k, l), this->format,
ImageDimensions::getMipDim(i - 1, this->width),
ImageDimensions::getMipDim(i, this->width),
ImageDimensions::getMipDim(i - 1, this->height),
ImageDimensions::getMipDim(i, this->height), this->
isSRGB(), filter);
1180 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, i, this->mipCount, j, this->frameCount, k, faceCount, this->width, this->height, l, this->depth) && mip.size() == length) {
1181 std::memcpy(outputDataPtr + offset, mip.data(), length);
1184#ifdef SOURCEPP_BUILD_WITH_THREADS
1186 if (std::thread::hardware_concurrency() > 0 && futures.size() >= std::thread::hardware_concurrency()) {
1187 for (
auto& future : futures) {
1196#ifdef SOURCEPP_BUILD_WITH_THREADS
1197 for (
auto& future : futures) {
1232 if (this->
version >= 1 && this->
version <= 4 && expectedLength < image->
data.size()) {
1235 if (expectedLength == image->data.size()) {
1245 this->
regenerateImageData(this->format, this->width, this->height, this->mipCount, this->frameCount, isCubemap ? ((this->
version >= 1 && this->
version <= 4) ? 7 : 6) : 1, this->depth);
1265 this->
regenerateImageData(this->format, this->width, this->height, this->mipCount, newFrameCount, isCubemap ? ((this->
version >= 1 && this->
version <= 4) ? 7 : 6) : 1, newDepth);
1286 static constexpr auto getReflectivityForImage = [](
const VTF& vtf, uint16_t frame, uint8_t face, uint16_t slice) {
1287 static constexpr auto getReflectivityForPixel = [](
const ImagePixel::RGBA8888* pixel) -> math::Vec3f {
1289 math::Vec3f ref{
static_cast<float>(pixel->r),
static_cast<float>(pixel->g),
static_cast<float>(pixel->b)};
1290 ref /= 255.f * 0.9f;
1299 for (uint64_t i = 0; i < rgba8888Data.size(); i += 4) {
1300 out += getReflectivityForPixel(
reinterpret_cast<ImagePixel::RGBA8888*
>(rgba8888Data.data() + i));
1307#ifdef SOURCEPP_BUILD_WITH_THREADS
1308 if (this->frameCount > 1 || faceCount > 1 || this->depth > 1) {
1309 std::vector<std::future<math::Vec3f>> futures;
1310 futures.reserve(this->frameCount * faceCount * this->depth);
1314 for (
int k = 0; k < faceCount; k++) {
1315 for (
int l = 0; l < this->
depth; l++) {
1316 futures.push_back(std::async(std::launch::async, [
this, j, k, l] {
1317 return getReflectivityForImage(*
this, j, k, l);
1319 if (std::thread::hardware_concurrency() > 0 && futures.size() >= std::thread::hardware_concurrency()) {
1320 for (
auto& future : futures) {
1329 for (
auto& future : futures) {
1334 this->
reflectivity = getReflectivityForImage(*
this, 0, 0, 0);
1339 for (
int k = 0; k < faceCount; k++) {
1340 for (
int l = 0; l < this->
depth; l++) {
1341 this->
reflectivity += getReflectivityForImage(*
this, j, k, l);
1386 for (
const auto& resource : this->
resources) {
1387 if (resource.type == type) {
1395 for (
auto& resource : this->
resources) {
1396 if (resource.type == type) {
1404 if (
const auto* resource = this->
getResource(type); resource && resource->
data.size() == data_.size()) {
1405 std::memcpy(resource->data.data(), data_.data(), data_.size());
1410 std::unordered_map<Resource::Type, std::pair<std::vector<std::byte>, uint64_t>> resourceData;
1411 for (
const auto& [type_, flags_, dataSpan] : this->
resources) {
1412 resourceData[type_] = {std::vector<std::byte>{dataSpan.begin(), dataSpan.end()}, 0};
1416 if (data_.empty()) {
1417 resourceData.erase(type);
1419 resourceData[type] = {{data_.begin(), data_.end()}, 0};
1424 BufferStream writer{this->data};
1427 if (!resourceData.contains(resourceType)) {
1430 auto& [specificResourceData, offset] = resourceData[resourceType];
1431 if (resourceType == type) {
1435 {this->data.data() + offset, specificResourceData.size()},
1438 *resourcePtr = newResource;
1440 this->resources.push_back(newResource);
1442 }
else if (!resourceData.contains(resourceType)) {
1445 offset = writer.tell();
1446 writer.write(specificResourceData);
1448 this->data.resize(writer.size());
1450 for (
auto& [type_, flags_, dataSpan] : this->resources) {
1451 if (resourceData.contains(type_)) {
1452 const auto& [specificResourceData, offset] = resourceData[type_];
1453 dataSpan = {this->data.data() + offset, specificResourceData.size()};
1463 if (!newWidth) { newWidth = 1; }
1464 if (!newHeight) { newHeight = 1; }
1465 if (!newMipCount) { newMipCount = 1; }
1466 if (!newFrameCount) { newFrameCount = 1; }
1467 if (!newFaceCount) { newFaceCount = 1; }
1468 if (!newDepth) { newDepth = 1; }
1471 if (this->format == newFormat && this->width == newWidth && this->height == newHeight && this->mipCount == newMipCount && this->frameCount == newFrameCount && faceCount == newFaceCount && this->depth == newDepth) {
1475 if (newMipCount > 1) {
1481 std::vector<std::byte> newImageData;
1483 if (this->format != newFormat && this->width == newWidth && this->height == newHeight && this->mipCount == newMipCount && this->frameCount == newFrameCount && faceCount == newFaceCount && this->depth == newDepth) {
1487 for (
int i = newMipCount - 1; i >= 0; i--) {
1488 for (
int j = 0; j < newFrameCount; j++) {
1489 for (
int k = 0; k < newFaceCount; k++) {
1490 for (
int l = 0; l < newDepth; l++) {
1493 std::vector<std::byte> image{imageSpan.begin(), imageSpan.end()};
1494 if (this->width != newWidth || this->height != newHeight) {
1495 image =
ImageConversion::resizeImageData(image, this->format,
ImageDimensions::getMipDim(i, this->width),
ImageDimensions::getMipDim(i, newWidth),
ImageDimensions::getMipDim(i, this->height),
ImageDimensions::getMipDim(i, newHeight), this->
isSRGB(), filter);
1497 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, i, newMipCount, j, newFrameCount, k, newFaceCount, newWidth, newHeight, l, newDepth) && image.size() == length) {
1498 std::memcpy(newImageData.data() + offset, image.data(), length);
1505 if (this->format != newFormat) {
1513 this->format = newFormat;
1514 this->width = newWidth;
1515 this->height = newHeight;
1516 this->mipCount = newMipCount;
1517 this->frameCount = newFrameCount;
1518 if (newFaceCount > 1) {
1521 this->
flags &= ~FLAG_V0_ENVMAP;
1523 this->depth = newDepth;
1530 if (palette->data.size() != targetSize) {
1531 std::vector<std::byte> paletteData{palette->data.begin(), palette->data.end()};
1532 paletteData.resize(targetSize);
1544 return palette->getDataAsPalette(frame);
1549std::vector<std::byte>
VTF::getParticleSheetFrameDataRaw(uint16_t& spriteWidth, uint16_t& spriteHeight, uint32_t shtSequenceID, uint32_t shtFrame, uint8_t shtBounds, uint8_t mip, uint16_t frame, uint8_t face, uint16_t slice)
const {
1558 auto sht = shtResource->getDataAsParticleSheet();
1559 const auto* sequence = sht.getSequenceFromID(shtSequenceID);
1560 if (!sequence || sequence->frames.size() <= shtFrame || shtBounds >= sht.getFrameBoundsCount()) {
1568 const auto& bounds = sequence->frames[shtFrame].bounds[shtBounds];
1569 uint16_t x1 = std::clamp<uint16_t>(std::floor(bounds.x1 *
static_cast<float>(this->getWidth(mip))), 0, this->getWidth(mip));
1570 uint16_t y1 = std::clamp<uint16_t>(std::ceil( bounds.y1 *
static_cast<float>(this->getHeight(mip))), 0, this->getHeight(mip));
1571 uint16_t x2 = std::clamp<uint16_t>(std::ceil( bounds.x2 *
static_cast<float>(this->getWidth(mip))), 0, this->getHeight(mip));
1572 uint16_t y2 = std::clamp<uint16_t>(std::floor(bounds.y2 *
static_cast<float>(this->getHeight(mip))), 0, this->getWidth(mip));
1574 if (x1 > x2) [[unlikely]] {
1577 if (y1 > y2) [[unlikely]] {
1580 spriteWidth = x2 - x1;
1581 spriteWidth = y2 - y1;
1583 const auto out =
ImageConversion::cropImageData(this->
getImageDataRaw(mip, frame, face, slice), this->
getFormat(), this->
getWidth(mip), spriteWidth, x1, this->
getHeight(mip), spriteHeight, y1);
1591std::vector<std::byte>
VTF::getParticleSheetFrameDataAs(
ImageFormat newFormat, uint16_t& spriteWidth, uint16_t& spriteHeight, uint32_t shtSequenceID, uint32_t shtFrame, uint8_t shtBounds, uint8_t mip, uint16_t frame, uint8_t face, uint16_t slice)
const {
1592 return ImageConversion::convertImageDataToFormat(this->
getParticleSheetFrameDataRaw(spriteWidth, spriteHeight, shtSequenceID, shtFrame, shtBounds, mip, frame, face, slice), this->
getFormat(), newFormat, spriteWidth, spriteHeight);
1595std::vector<std::byte>
VTF::getParticleSheetFrameDataAsRGBA8888(uint16_t& spriteWidth, uint16_t& spriteHeight, uint32_t shtSequenceID, uint32_t shtFrame, uint8_t shtBounds, uint8_t mip, uint16_t frame, uint8_t face, uint16_t slice)
const {
1600 std::vector<std::byte> particleSheetData;
1601 BufferStream writer{particleSheetData};
1603 const auto bakedSheet = value.
bake();
1604 writer.write<uint32_t>(bakedSheet.size()).write(bakedSheet);
1605 particleSheetData.resize(writer.size());
1624 BufferStream writer{&lodData,
sizeof(lodData)};
1626 writer << u << v << u360 << v360;
1644 std::vector<std::byte> keyValuesData;
1645 BufferStream writer{keyValuesData};
1647 writer.write<uint32_t>(value.size()).write(value,
false);
1648 keyValuesData.resize(writer.size());
1658 std::vector<std::byte> hotspotData;
1659 BufferStream writer{hotspotData};
1661 const auto bakedHotspotData = value.
bake();
1662 writer.write<uint32_t>(bakedHotspotData.size()).write(bakedHotspotData);
1663 hotspotData.resize(writer.size());
1700 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, frame, this->frameCount, face, this->
getFaceCount(), this->width, this->height, slice, this->depth)) {
1701 return imageResource->data.subspan(offset, length);
1708 const auto rawImageData = this->
getImageDataRaw(mip, frame, face, slice);
1709 if (rawImageData.empty()) {
1725 if (imageData_.empty()) {
1730 uint16_t resizedWidth = width_, resizedHeight = height_;
1737 mip = newMipCount - 1;
1739 if (face > 6 || (face == 6 && (this->version < 1 || this->
version > 4))) {
1742 this->
regenerateImageData(format_, resizedWidth, resizedHeight, mip + 1, frame + 1, face ? (face < 5 ? 5 : face) : 0, slice + 1);
1746 if (this->mipCount <= mip || this->
frameCount <= frame || faceCount <= face || this->
depth <= slice) {
1751 if (!imageResource) {
1754 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, frame, this->frameCount, face, faceCount, this->width, this->height, slice, this->depth)) {
1755 std::vector<std::byte> image{imageData_.begin(), imageData_.end()};
1758 if (width_ != newWidth || height_ != newHeight) {
1761 if (format_ != this->format) {
1764 std::memcpy(imageResource->data.data() + offset, image.data(), image.size());
1771 int inputWidth, inputHeight, inputFrameCount;
1775 if (imageData_.empty() || inputFormat ==
ImageFormat::EMPTY || !inputWidth || !inputHeight || !inputFrameCount) {
1780 if (inputFrameCount == 1) {
1781 return this->
setImage(imageData_, inputFormat, inputWidth, inputHeight, filter, mip, frame, face, slice, quality);
1785 bool allSuccess =
true;
1787 for (
int currentFrame = 0; currentFrame < inputFrameCount; currentFrame++) {
1788 if (!this->
setImage({imageData_.data() + currentFrame * frameSize, imageData_.data() + currentFrame * frameSize + frameSize}, inputFormat, inputWidth, inputHeight, filter, mip, frame + currentFrame, face, slice, quality)) {
1791 if (currentFrame == 0 && this->frameCount < frame + inputFrameCount) {
1804 if (
auto data_ = this->
saveImageToFile(mip, frame, face, slice, fileFormat); !data_.empty()) {
1816 return thumbnailResource->data;
1823 if (rawThumbnailData.empty()) {
1845 int inputWidth, inputHeight, inputFrameCount;
1849 if (imageData_.empty() || inputFormat ==
ImageFormat::EMPTY || !inputWidth || !inputHeight || !inputFrameCount) {
1854 if (inputFrameCount == 1) {
1855 this->
setThumbnail(imageData_, inputFormat, inputWidth, inputHeight, quality);
1861 this->
setThumbnail({imageData_.data(), frameSize}, inputFormat, inputWidth, inputHeight, quality);
1872 this->
setResourceInternal(
Resource::TYPE_THUMBNAIL_DATA,
ImageConversion::convertImageDataToFormat(
ImageConversion::resizeImageData(this->
getImageDataRaw(), this->format, this->width, this->
thumbnailWidth, this->height, this->
thumbnailHeight, this->
isSRGB(), filter), this->format, this->
thumbnailFormat, this->
thumbnailWidth, this->
thumbnailHeight, quality));
1899 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->
fallbackMipCount, frame, this->frameCount, face, this->
getFaceCount(), this->
fallbackWidth, this->
fallbackHeight)) {
1900 return fallbackResource->data.subspan(offset, length);
1908 if (rawFallbackData.empty()) {
1929 if (!imageResource) {
1939 std::vector<std::byte> fallbackData;
1943 for (
int k = 0; k < faceCount; k++) {
1944 auto mip =
ImageConversion::resizeImageData(this->
getImageDataRaw(0, j, k, 0), this->format, this->width,
ImageDimensions::getMipDim(i, this->
fallbackWidth), this->height,
ImageDimensions::getMipDim(i, this->
fallbackHeight), this->
isSRGB(), filter);
1945 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, i, this->
fallbackMipCount, j, this->frameCount, k, faceCount, this->
fallbackWidth, this->
fallbackHeight) && mip.size() == length) {
1946 std::memcpy(fallbackData.data() + offset, mip.data(), length);
1966 if (
auto data_ = this->
saveFallbackToFile(mip, frame, face, fileFormat); !data_.empty()) {
1987 uint64_t vtfSize = 0;
1989 switch (this->platform) {
1996 vtfSize +=
sizeof(uint64_t);
1997 vtfSize +=
sizeof(uint32_t) * (2 + this->mipCount * this->frameCount * this->
getFaceCount());
2002 vtfSize += 11 +
sizeof(uint32_t) +
sizeof(uint64_t) * this->
getResources().size();
2003 for (
const auto& [resourceType, resourceFlags, resourceData] : this->
getResources()) {
2005 vtfSize += resourceData.size();
2009 vtfSize +=
sizeof(uint16_t);
2012 vtfSize +=
sizeof(uint32_t) * 9 +
sizeof(
float) * 4 +
sizeof(uint16_t) * 4 +
sizeof(uint8_t) * 3;
2018 vtfSize += thumbnailResource->data.size();
2022 vtfSize += imageResource->data.size();
2025 vtfSize +=
static_cast<uint64_t
>(
static_cast<double>(imageResource->data.size()) / 1.75);
2034 vtfSize +=
sizeof(uint32_t) * 6 +
sizeof(
float) * 4 +
sizeof(uint16_t) * 6 +
sizeof(uint8_t) * 6;
2037 vtfSize += thumbnailResource->data.size();
2041 vtfSize += paletteResource->data.size();
2051 if (vtfSize > 512) {
2056 vtfSize +=
sizeof(uint32_t);
2059 vtfSize +=
sizeof(uint32_t) * 7 +
sizeof(
float) * 4 +
sizeof(uint16_t) * 5 +
sizeof(uint8_t) * 6;
2060 vtfSize +=
sizeof(uint64_t) * this->
getResources().size();
2062 for (
const auto& [resourceType, resourceFlags, resourceData] : this->
getResources()) {
2064 vtfSize += resourceData.size();
2069 vtfSize += imageResource->data.size();
2072 vtfSize +=
static_cast<uint64_t
>(
static_cast<double>(imageResource->data.size()) / 1.75);
2081 std::vector<std::byte> out;
2082 BufferStream writer{out};
2086 BufferStream::swap_endian(
reinterpret_cast<uint32_t*
>(&type));
2088 writer_.write<uint32_t>(type);
2089 const auto resourceOffsetPos = writer_.tell();
2090 writer_.seek(0, std::ios::end);
2091 const auto resourceOffsetValue = writer_.tell();
2092 writer_.write(
data);
2093 writer_.seek_u(resourceOffsetPos).write<uint32_t>(resourceOffsetValue);
2097 auto bakeFormat = this->
format;
2098 auto bakeFlags = this->
flags;
2108 switch (this->platform) {
2117 const auto headerLengthPos = writer.tell();
2118 writer.write<uint32_t>(0);
2122 .write(this->height)
2124 .write(this->frameCount)
2131 .write(this->mipCount)
2137 writer << this->
depth;
2142 const auto headerSize = writer.tell();
2143 writer.seek_u(headerLengthPos).write<uint32_t>(headerSize).seek_u(headerSize);
2146 writer.write(thumbnailResource->data);
2149 writer.write(imageResource->data);
2152 std::vector<std::byte> auxCompressionResourceData;
2153 std::vector<std::byte> compressedImageResourceData;
2154 bool hasAuxCompression =
false;
2157 if (hasAuxCompression) {
2159 auxCompressionResourceData.resize((this->mipCount * this->frameCount * faceCount + 2) *
sizeof(uint32_t));
2160 BufferStream auxWriter{auxCompressionResourceData,
false};
2167 .write<uint32_t>(auxCompressionResourceData.size() -
sizeof(uint32_t))
2171 for (
int i = this->mipCount - 1; i >= 0; i--) {
2173 for (
int k = 0; k < faceCount; k++) {
2174 if (uint32_t offset, length;
ImageFormatDetails::getDataPosition(offset, length, this->format, i, this->mipCount, j, this->frameCount, k, faceCount, this->width, this->height, 0, this->depth)) {
2176 compressedImageResourceData.insert(compressedImageResourceData.end(), compressedData.begin(), compressedData.end());
2177 auxWriter.write<uint32_t>(compressedData.size());
2185 writer.pad(3).write<uint32_t>(this->
getResources().size() + hasAuxCompression).pad(8);
2187 const auto resourceStart = writer.tell();
2188 const auto headerSize = resourceStart + ((this->
getResources().size() + hasAuxCompression) *
sizeof(uint64_t));
2189 writer.seek_u(headerLengthPos).write<uint32_t>(headerSize).seek_u(resourceStart);
2190 while (writer.tell() < headerSize) {
2191 writer.write<uint64_t>(0);
2193 writer.seek_u(resourceStart);
2197 writeNonLocalResource(writer, resourceType, auxCompressionResourceData, this->platform);
2199 writeNonLocalResource(writer, resourceType, compressedImageResourceData, this->platform);
2200 }
else if (
const auto* resource = this->
getResource(resourceType)) {
2203 writer.write(resource->data);
2205 writeNonLocalResource(writer, resource->type, resource->data, this->platform);
2214 writer.write<uint32_t>(0);
2216 const auto headerSizePos = writer.tell();
2221 .write(this->height)
2223 .write(this->frameCount);
2224 const auto preloadSizePos = writer.tell();
2225 writer.write<uint16_t>(0);
2226 const auto imageOffsetPos = writer.tell();
2233 .write(this->format)
2241 const auto headerSize = writer.tell();
2242 writer.seek_u(headerSizePos).write<uint32_t>(headerSize).seek_u(headerSize);
2245 writer.write(thumbnailResource->data);
2250 writer.write(paletteResource->data);
2254 bool hasFallbackResource =
false;
2256 hasFallbackResource =
true;
2258 auto reorderedFallbackData = ::convertBetweenDDSAndVTFMipOrderForXBOX<false>(
false, fallbackResource->data, this->format, this->fallbackMipCount, this->frameCount, this->getFaceCount(), this->fallbackWidth, this->fallbackHeight, 1, ok);
2261 writer.write(reorderedFallbackData);
2263 writer.pad(fallbackResource->data.size());
2267 const auto preloadSize = writer.tell();
2268 writer.seek_u(preloadSizePos).write<uint32_t>(preloadSize).seek_u(preloadSize);
2270 if (hasFallbackResource) {
2273 const auto imageOffset = writer.tell();
2274 writer.seek_u(imageOffsetPos).write<uint16_t>(imageOffset).seek_u(imageOffset);
2278 auto reorderedImageData = ::convertBetweenDDSAndVTFMipOrderForXBOX<false>(
true, imageResource->data, this->format, this->mipCount, this->frameCount, this->getFaceCount(), this->width, this->height, this->depth, ok);
2280 ::swapImageDataEndianForConsole<false>(reorderedImageData, this->format, this->mipCount, this->frameCount, this->
getFaceCount(), this->width, this->height, this->depth, this->platform);
2281 writer.write(reorderedImageData);
2283 writer.pad(imageResource->data.size());
2286 if (writer.tell() > 512) {
2296 writer.set_big_endian(
true);
2300 writer.set_big_endian(
true);
2303 writer.write<uint32_t>(8);
2305 const auto headerLengthPos = writer.tell();
2310 .write(this->height)
2312 .write(this->frameCount);
2313 const auto preloadPos = writer.tell();
2323 .write<uint8_t>(std::clamp(
static_cast<int>(std::roundf(this->
reflectivity[0] * 255)), 0, 255))
2324 .write<uint8_t>(std::clamp(
static_cast<int>(std::roundf(this->
reflectivity[1] * 255)), 0, 255))
2325 .write<uint8_t>(std::clamp(
static_cast<int>(std::roundf(this->
reflectivity[2] * 255)), 0, 255))
2326 .write<uint8_t>(255);
2327 const auto compressionPos = writer.tell();
2328 writer.write<uint32_t>(0);
2332 writer.write<uint32_t>(0);
2337 std::vector<std::byte> imageResourceData;
2338 bool hasCompression =
false;
2340 imageResourceData.assign(imageResource->data.begin(), imageResource->data.end());
2341 ::swapImageDataEndianForConsole<false>(imageResourceData, this->format, this->mipCount, this->frameCount, this->
getFaceCount(), this->width, this->height, this->depth, this->platform);
2345 if (hasCompression) {
2351 fixedCompressionLevel = 6;
2354 imageResourceData.assign(compressedData->begin(), compressedData->end());
2356 hasCompression =
false;
2362 const auto resourceStart = writer.tell();
2363 const auto headerSize = resourceStart + (this->
getResources().size() *
sizeof(uint64_t));
2364 writer.seek_u(headerLengthPos).write<uint32_t>(headerSize).seek_u(resourceStart);
2365 while (writer.tell() < headerSize) {
2366 writer.write<uint64_t>(0);
2368 writer.seek_u(resourceStart);
2372 auto curPos = writer.tell();
2373 const auto imagePos = writer.seek(0, std::ios::end).tell();
2374 writer.seek_u(preloadPos).write(std::max<uint16_t>(imagePos, 2048)).seek_u(curPos);
2376 writeNonLocalResource(writer, resourceType, imageResourceData, this->platform);
2378 if (hasCompression) {
2379 curPos = writer.tell();
2380 writer.seek_u(compressionPos).write<uint32_t>(imageResourceData.size()).seek_u(curPos);
2382 }
else if (
const auto* resource = this->
getResource(resourceType)) {
2383 std::vector<std::byte> resData{resource->data.begin(), resource->data.end()};
2388 BufferStream::swap_endian(
reinterpret_cast<uint32_t*
>(resData.data()));
2392 writer.set_big_endian(
false);
2394 writer.set_big_endian(
true);
2395 writer.write(resData);
2397 writeNonLocalResource(writer, resource->type, resData, this->platform);
2404 out.resize(writer.size());
#define SOURCEPP_DEBUG_BREAK
Create a breakpoint in debug.
std::vector< std::byte > bake() const
std::vector< std::byte > bake() const
void setImageHeightResizeMethod(ImageConversion::ResizeMethod imageHeightResizeMethod_)
bool setFrameFaceAndDepth(uint16_t newFrameCount, bool isCubeMap, uint16_t newDepth=1)
void removeKeyValuesDataResource()
void computeFallback(ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT)
uint8_t getFallbackHeight() const
CompressionMethod compressionMethod
void computeReflectivity()
uint8_t getThumbnailWidth() const
std::vector< std::byte > saveThumbnailToFile(ImageConversion::FileFormat fileFormat=ImageConversion::FileFormat::DEFAULT) const
ImageFormat getFormat() const
void setPlatform(Platform newPlatform)
uint16_t getHeight(uint8_t mip=0) const
VTF & operator=(const VTF &other)
uint8_t getFallbackWidth() const
sourcepp::math::Vec3f reflectivity
uint16_t getWidth(uint8_t mip=0) const
void setThumbnail(std::span< const std::byte > imageData_, ImageFormat format_, uint16_t width_, uint16_t height_, float quality=ImageConversion::DEFAULT_COMPRESSED_QUALITY)
bool setRecommendedMipCount()
static bool createInternal(VTF &writer, CreationOptions options)
std::vector< std::byte > getParticleSheetFrameDataAsRGBA8888(uint16_t &spriteWidth, uint16_t &spriteHeight, 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) const
This is a convenience function. You're best off uploading the bounds to the GPU and scaling the UV th...
void regenerateImageData(ImageFormat newFormat, uint16_t newWidth, uint16_t newHeight, uint8_t newMipCount, uint16_t newFrameCount, uint8_t newFaceCount, uint16_t newDepth, ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT, float quality=ImageConversion::DEFAULT_COMPRESSED_QUALITY)
bool hasFallbackData() const
void computeMips(ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT)
void setImageWidthResizeMethod(ImageConversion::ResizeMethod imageWidthResizeMethod_)
void setCompressionLevel(int16_t newCompressionLevel)
uint16_t getDepth() const
ImageConversion::ResizeMethod imageHeightResizeMethod
void setFormat(ImageFormat newFormat, ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT, float quality=ImageConversion::DEFAULT_COMPRESSED_QUALITY)
void setImageResizeMethods(ImageConversion::ResizeMethod imageWidthResizeMethod_, ImageConversion::ResizeMethod imageHeightResizeMethod_)
std::vector< std::byte > getParticleSheetFrameDataAs(ImageFormat newFormat, uint16_t &spriteWidth, uint16_t &spriteHeight, 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) const
This is a convenience function. You're best off uploading the bounds to the GPU and scaling the UV th...
float getBumpMapScale() const
void computeTransparencyFlags()
std::vector< std::byte > saveFallbackToFile(uint8_t mip=0, uint16_t frame=0, uint8_t face=0, ImageConversion::FileFormat fileFormat=ImageConversion::FileFormat::DEFAULT) const
std::vector< std::byte > data
Platform getPlatform() const
std::vector< std::byte > getPaletteResourceFrame(uint16_t frame=0) const
void addFlags(uint32_t flags_)
uint8_t getFaceCount() const
ImageFormat thumbnailFormat
void setFlags(uint32_t flags_)
void setSize(uint16_t newWidth, uint16_t newHeight, ImageConversion::ResizeFilter filter)
ImageConversion::ResizeMethod getImageHeightResizeMethod() const
void setBumpMapScale(float newBumpMapScale)
ImageFormat getThumbnailFormat() const
uint16_t getStartFrame() const
static constexpr auto FORMAT_DEFAULT
This value is only valid when passed to VTF::create through CreationOptions or VTF::setFormat.
bool hasThumbnailData() const
void setReflectivity(sourcepp::math::Vec3f newReflectivity)
const std::vector< Resource > & getResources() const
void removeHotspotDataResource()
void setVersion(uint32_t newVersion)
std::vector< std::byte > saveImageToFile(uint8_t mip=0, uint16_t frame=0, uint8_t face=0, uint16_t slice=0, ImageConversion::FileFormat fileFormat=ImageConversion::FileFormat::DEFAULT) const
Resource * getResourceInternal(Resource::Type type)
void computeThumbnail(ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT, float quality=ImageConversion::DEFAULT_COMPRESSED_QUALITY)
std::vector< std::byte > bake() const
bool hasImageData() const
ImageConversion::ResizeMethod imageWidthResizeMethod
std::span< const std::byte > getFallbackDataRaw(uint8_t mip=0, uint16_t frame=0, uint8_t face=0) const
void removeParticleSheetResource()
std::vector< std::byte > getFallbackDataAs(ImageFormat newFormat, uint8_t mip=0, uint16_t frame=0, uint8_t face=0) const
std::vector< std::byte > getImageDataAsRGBA8888(uint8_t mip=0, uint16_t frame=0, uint8_t face=0, uint16_t slice=0) const
uint16_t getFrameCount() const
uint8_t getMipCount() const
void setLODResource(uint8_t u, uint8_t v, uint8_t u360=0, uint8_t v360=0)
void removeResourceInternal(Resource::Type type)
std::vector< std::byte > getFallbackDataAsRGBA8888(uint8_t mip=0, uint16_t frame=0, uint8_t face=0) const
bool setFrameCount(uint16_t newFrameCount)
ImageConversion::ResizeMethod getImageWidthResizeMethod() const
std::vector< std::byte > getImageDataAs(ImageFormat newFormat, uint8_t mip=0, uint16_t frame=0, uint8_t face=0, uint16_t slice=0) const
static ImageFormat getDefaultCompressedFormat(ImageFormat inputFormat, uint32_t version, bool isCubeMap)
void setStartFrame(uint16_t newStartFrame)
std::vector< std::byte > getThumbnailDataAs(ImageFormat newFormat) const
std::span< const std::byte > getImageDataRaw(uint8_t mip=0, uint16_t frame=0, uint8_t face=0, uint16_t slice=0) const
sourcepp::math::Vec3f getReflectivity() const
uint8_t getFallbackMipCount() const
std::vector< std::byte > getThumbnailDataAsRGBA8888() const
uint8_t getConsoleMipScale() const
static constexpr auto FORMAT_UNCHANGED
This value is only valid when passed to VTF::create through CreationOptions.
std::span< const std::byte > getThumbnailDataRaw() const
bool setMipCount(uint8_t newMipCount)
void setExtendedFlagsResource(uint32_t value)
CompressionMethod getCompressionMethod() const
void setCRCResource(uint32_t value)
void setCompressionMethod(CompressionMethod newCompressionMethod)
bool setFaceCount(bool isCubeMap)
uint8_t getThumbnailHeight() const
void setConsoleMipScale(uint8_t consoleMipScale_)
const Resource * getResource(Resource::Type type) const
uint32_t getFlags() const
void setParticleSheetResource(const SHT &value)
void setKeyValuesDataResource(const std::string &value)
@ FLAG_V0_MULTI_BIT_ALPHA
uint32_t getVersion() const
int16_t getCompressionLevel() const
void setHotspotDataResource(const HOT &value)
void removeFlags(uint32_t flags_)
void setResourceInternal(Resource::Type type, std::span< const std::byte > data_)
bool setDepth(uint16_t newDepth)
uint64_t estimateBakeSize() const
std::vector< Resource > resources
bool setImage(std::span< const std::byte > imageData_, ImageFormat format_, uint16_t width_, uint16_t height_, ImageConversion::ResizeFilter filter=ImageConversion::ResizeFilter::DEFAULT, uint8_t mip=0, uint16_t frame=0, uint8_t face=0, uint16_t slice=0, float quality=ImageConversion::DEFAULT_COMPRESSED_QUALITY)
std::vector< std::byte > getParticleSheetFrameDataRaw(uint16_t &spriteWidth, uint16_t &spriteHeight, 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) const
This is a convenience function. You're best off uploading the bounds to the GPU and scaling the UV th...
void removeExtendedFlagsResource()
static bool create(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, const std::string &vtfPath, const CreationOptions &options)
std::optional< std::vector< std::byte > > compressValveLZMA(std::span< const std::byte > data, uint8_t compressLevel=6)
constexpr auto VALVE_LZMA_SIGNATURE
std::optional< std::vector< std::byte > > decompressValveLZMA(std::span< const std::byte > data)
std::vector< std::byte > readFileBuffer(const std::string &filepath, std::size_t startOffset=0)
bool writeFileBuffer(const std::string &filepath, std::span< const std::byte > buffer)
constexpr uint16_t paddingForAlignment(uint16_t alignment, uint64_t n)
constexpr uint32_t log2ceil(uint32_t value)
std::vector< std::byte > convertFileToImageData(std::span< const std::byte > fileData, ImageFormat &format, int &width, int &height, int &frameCount)
std::vector< std::byte > convertImageDataToFile(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, FileFormat fileFormat=FileFormat::DEFAULT)
Converts image data to the given file format (PNG or EXR by default).
void setResizedDims(uint16_t &width, ResizeMethod widthResize, uint16_t &height, ResizeMethod heightResize)
Set the new image dimensions given a resize method.
std::vector< std::byte > convertImageDataToFormat(std::span< const std::byte > imageData, ImageFormat oldFormat, ImageFormat newFormat, uint16_t width, uint16_t height, float quality=DEFAULT_COMPRESSED_QUALITY)
Converts an image from one format to another.
std::vector< std::byte > convertSeveralImageDataToFormat(std::span< const std::byte > imageData, ImageFormat oldFormat, ImageFormat newFormat, uint8_t mipCount, uint16_t frameCount, uint8_t faceCount, uint16_t width, uint16_t height, uint16_t depth, float quality=DEFAULT_COMPRESSED_QUALITY)
Converts several images from one format to another.
std::vector< std::byte > gammaCorrectImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height, float gamma)
Perform gamma correction on the given image data. Will not perform gamma correction if the input imag...
std::vector< std::byte > cropImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t xOffset, uint16_t height, uint16_t newHeight, uint16_t yOffset)
Crops the given image to the new dimensions. If the image format is compressed it will be converted t...
std::vector< std::byte > invertGreenChannelForImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t height)
Invert the green channel. Meant for converting normal maps between OpenGL and DirectX formats.
std::vector< std::byte > resizeImageData(std::span< const std::byte > imageData, ImageFormat format, uint16_t width, uint16_t newWidth, uint16_t height, uint16_t newHeight, bool srgb, ResizeFilter filter, ResizeEdge edge=ResizeEdge::CLAMP)
Resize given image data to the new dimensions.
constexpr uint32_t getMipDim(uint8_t mip, uint16_t dim)
constexpr uint8_t getActualMipCountForDimsOnConsole(uint16_t width, uint16_t height, uint16_t depth=1)
constexpr uint8_t getRecommendedMipCountForDims(ImageFormat format, uint16_t width, uint16_t height)
std::vector< std::byte > convertP8ImageDataToBGRA8888(std::span< const std::byte > paletteData, std::span< const std::byte > imageData)
Converts a paletted image to something usable.
constexpr uint32_t VTF_SIGNATURE
constexpr uint32_t XTF_SIGNATURE
constexpr uint32_t VTFX_SIGNATURE
constexpr uint32_t VTF3_SIGNATURE
std::variant< std::monostate, SHT, uint32_t, std::tuple< uint8_t, uint8_t, uint8_t, uint8_t >, std::string, HOT > ConvertedData
ConvertedData convertData() const
static consteval std::array< Type, 11 > getOrder()
@ TYPE_PARTICLE_SHEET_DATA
std::span< std::byte > data
uint16_t initialFrameCount
float compressedFormatQuality
bool computeTransparencyFlags
ImageConversion::ResizeFilter filter
ImageConversion::ResizeMethod heightResizeMethod
CompressionMethod compressionMethod
ImageConversion::ResizeMethod widthResizeMethod