Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions include/vtfpp/DDS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once

#include <array>
#include <filesystem>

#include <sourcepp/parser/Binary.h>
#include <sourcepp/Macros.h>
#include <sourcepp/Math.h>

#include "ImageConversion.h"

namespace vtfpp {

constexpr uint32_t DDS_SIGNATURE = sourcepp::parser::binary::makeFourCC("DDS ");

class DDS {
public:
struct CreationOptions {
ImageFormat outputFormat = FORMAT_DEFAULT;
float compressedFormatQuality = ImageConversion::DEFAULT_COMPRESSED_QUALITY;
ImageConversion::ResizeMethod widthResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER;
ImageConversion::ResizeMethod heightResizeMethod = ImageConversion::ResizeMethod::POWER_OF_TWO_BIGGER;
ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::DEFAULT;
ImageConversion::ResizeBounds resizeBounds;
bool isCubeMap = false;
uint16_t initialDepth = 1;
bool computeMips = true;
};

DDS();

explicit DDS(std::vector<std::byte>&& ddsData);

explicit DDS(std::span<const std::byte> ddsData);

explicit DDS(const std::filesystem::path& ddsPath);

DDS(const DDS& other);

DDS& operator=(const DDS& other);

DDS(DDS&&) noexcept = default;

DDS& operator=(DDS&&) noexcept = default;

[[nodiscard]] explicit operator bool() const;

static bool create(std::span<const std::byte> imageData, ImageFormat format, uint16_t width, uint16_t height, const std::filesystem::path& ddsPath, const CreationOptions& options);

static bool create(ImageFormat format, uint16_t width, uint16_t height, const std::filesystem::path& ddsPath, const CreationOptions& options);

[[nodiscard]] static DDS create(std::span<const std::byte> imageData, ImageFormat format, uint16_t width, uint16_t height, const CreationOptions& options);

[[nodiscard]] static DDS create(ImageFormat format, uint16_t width, uint16_t height, const CreationOptions& options);

static bool create(const std::filesystem::path& imagePath, const std::filesystem::path& ddsPath, const CreationOptions& options);

[[nodiscard]] static DDS create(const std::filesystem::path& imagePath, const CreationOptions& options);

[[nodiscard]] uint16_t getWidth() const;

[[nodiscard]] uint16_t getHeight() const;

[[nodiscard]] bool hasImageData() const;

[[nodiscard]] ImageFormat getFormat() const;

[[nodiscard]] uint8_t getMipCount() const;

[[nodiscard]] std::span<const std::byte> getImageDataRaw(uint8_t mip = 0) const;

[[nodiscard]] std::span<std::byte> getImageDataRaw(uint8_t mip = 0);

[[nodiscard]] std::vector<std::byte> getImageDataAs(ImageFormat newFormat, uint8_t mip = 0) const;

[[nodiscard]] std::vector<std::byte> getImageDataAsRGBA8888(uint8_t mip = 0) const;

protected:
std::vector<std::byte> data;
std::span<std::byte> imageData;
uint16_t width = 0;
uint16_t height = 0;
ImageFormat format = ImageFormat::EMPTY;
uint8_t mipCount = 1;
bool opened = false;
};

} // namespace vtfpp
190 changes: 190 additions & 0 deletions src/vtfpp/DDS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#include <vtfpp/DDS.h>

#include <vector>

#include <BufferStream.h>
#include <sourcepp/FS.h>

using namespace sourcepp;
using namespace vtfpp;

constexpr uint32_t DXT1_SIGNATURE = sourcepp::parser::binary::makeFourCC("DXT1");
constexpr uint32_t DXT2_SIGNATURE = sourcepp::parser::binary::makeFourCC("DXT2");
constexpr uint32_t DXT3_SIGNATURE = sourcepp::parser::binary::makeFourCC("DXT3");
constexpr uint32_t DXT4_SIGNATURE = sourcepp::parser::binary::makeFourCC("DXT4");
constexpr uint32_t DXT5_SIGNATURE = sourcepp::parser::binary::makeFourCC("DXT5");
constexpr uint32_t DX10_SIGNATURE = sourcepp::parser::binary::makeFourCC("DX10");

DDS::DDS(std::span<const std::byte> ddsData) {
BufferStreamReadOnly stream{ddsData};

if (stream.read<uint32_t>() != DDS_SIGNATURE) {
return;
}

const auto DDS_PIXELFORMAT_LENGTH = sizeof(uint32_t) * 8;
const auto DDS_HEADER_LENGTH = sizeof(uint32_t) * 23 + sizeof(DDS_PIXELFORMAT_LENGTH);

// Check header size
uint32_t headerLength = stream.read<uint32_t>();
if (headerLength != DDS_HEADER_LENGTH) {
return;
}

// Read header fields
uint32_t headerFlags = stream.read<uint32_t>();
uint32_t imageHeight = stream.read<uint32_t>();
uint32_t imageWidth = stream.read<uint32_t>();
uint32_t imagePitch = stream.read<uint32_t>();
uint32_t imageDepth = stream.read<uint32_t>();
uint32_t imageMips = stream.read<uint32_t>();

// Skip reserved section
stream.skip_u(sizeof(uint32_t) * 11);

// Check pixel format subheader size
uint32_t pixelHeaderLength = stream.read<uint32_t>();
if (pixelHeaderLength != DDS_PIXELFORMAT_LENGTH) {
return;
}

// Read pixel format subheader fields
uint32_t pixelHeaderFlags = stream.read<uint32_t>();
uint32_t pixelHeaderFourCC = stream.read<uint32_t>();
uint32_t pixelHeaderBitCount = stream.read<uint32_t>();
uint32_t pixelHeaderRedBitMask = stream.read<uint32_t>();
uint32_t pixelHeaderGreenBitMask = stream.read<uint32_t>();
uint32_t pixelHeaderBlueBitMask = stream.read<uint32_t>();
uint32_t pixelHeaderAlphaBitMask = stream.read<uint32_t>();

// Read the rest of the main header
uint32_t headerCaps0 = stream.read<uint32_t>();
uint32_t headerCaps1 = stream.read<uint32_t>();
uint32_t headerCaps2 = stream.read<uint32_t>();
uint32_t headerCaps3 = stream.read<uint32_t>();

// Skip reserved value
stream.skip_u(sizeof(uint32_t));

ImageFormat imageFormat = ImageFormat::EMPTY;

// Check if we need to parse DX10 header
if (pixelHeaderFourCC == DX10_SIGNATURE) {
const auto DX10_HEADER_LENGTH = sizeof(uint32_t) * 5;

uint32_t dx10HeaderFormat = stream.read<uint32_t>();
uint32_t dx10HeaderResourceDimension = stream.read<uint32_t>();
uint32_t dx10HeaderMiscFlags0 = stream.read<uint32_t>();
uint32_t dx10HeaderArraySize = stream.read<uint32_t>();
uint32_t dx10HeaderMiscFlags1 = stream.read<uint32_t>();

} else {
// Get image format
switch (pixelHeaderFourCC) {
case DXT1_SIGNATURE:
imageFormat = ImageFormat::DXT1;
break;
case DXT2_SIGNATURE:
imageFormat = ImageFormat::DXT3;
break;
case DXT3_SIGNATURE:
imageFormat = ImageFormat::DXT3;
break;
case DXT4_SIGNATURE:
imageFormat = ImageFormat::DXT5;
break;
case DXT5_SIGNATURE:
imageFormat = ImageFormat::DXT5;
break;
}
}

// Couldn't determine image format
if (imageFormat == ImageFormat::EMPTY) {
return;
}

// Setup final fields
this->width = imageWidth;
this->height = imageHeight;
this->format = imageFormat;
this->mipCount = imageMips;

// Read image data
this->data = stream.read_bytes(stream.size() - stream.tell());
this->imageData = {this->data.begin(), this->data.end()};
}

DDS::DDS() {
this->opened = true;
}

DDS::DDS(const DDS& other) {
*this = other;
}

DDS& DDS::operator=(const DDS& other) {
this->opened = other.opened;
this->data = other.data;
this->imageData = {this->data.begin(), this->data.end()};
this->width = other.width;
this->height = other.height;
this->format = other.format;
this->mipCount = other.mipCount;

return *this;
}

DDS::DDS(const std::filesystem::path& ddsPath)
: DDS(fs::readFileBuffer(ddsPath)) {}

DDS::operator bool() const {
return this->opened;
}

uint16_t DDS::getWidth() const {
return this->width;
}

uint16_t DDS::getHeight() const {
return this->height;
}

bool DDS::hasImageData() const {
return this->format != ImageFormat::EMPTY && this->width > 0 && this->height > 0;
}

ImageFormat DDS::getFormat() const {
return this->format;
}

uint8_t DDS::getMipCount() const {
return this->mipCount;
}

std::span<const std::byte> DDS::getImageDataRaw(uint8_t mip) const {
if (uint32_t offset, length; ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, 0, 1, 0, 1, this->width, this->height, 0, 1)) {
return this->imageData.subspan(offset, length);
}
return {};
}

std::span<std::byte> DDS::getImageDataRaw(uint8_t mip) {
if (uint32_t offset, length; ImageFormatDetails::getDataPosition(offset, length, this->format, mip, this->mipCount, 0, 1, 0, 1, this->width, this->height, 0, 1)) {
return this->imageData.subspan(offset, length);
}
return {};
}

std::vector<std::byte> DDS::getImageDataAs(ImageFormat newFormat, uint8_t mip) const {
const auto rawImageData = this->getImageDataRaw(mip);
if (rawImageData.empty()) {
return {};
}
const auto [mipWidth, mipHeight] = ImageDimensions::getMipDims(mip, this->width, this->height);
return ImageConversion::convertImageDataToFormat(rawImageData, this->format, newFormat, mipWidth, mipHeight);
}

std::vector<std::byte> DDS::getImageDataAsRGBA8888(uint8_t mip) const {
return this->getImageDataAs(ImageFormat::RGBA8888, mip);
}
110 changes: 56 additions & 54 deletions src/vtfpp/_vtfpp.cmake
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
add_pretty_parser(vtfpp
DEPS bcdec libzstd_static miniz sourcepp_compression sourcepp_parser sourcepp_stb
PRECOMPILED_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/DistanceMapping.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/HOT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageConversion.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageFormats.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImagePixel.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageQuantize.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/PPL.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/PSFrames.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/SHT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/TTX.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/VBF.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/VTF.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/vtfpp.h"
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/DistanceMapping.cpp"
"${CMAKE_CURRENT_LIST_DIR}/HOT.cpp"
"${CMAKE_CURRENT_LIST_DIR}/ImageConversion.cpp"
"${CMAKE_CURRENT_LIST_DIR}/ImageQuantize.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PPL.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PSFrames.cpp"
"${CMAKE_CURRENT_LIST_DIR}/SHT.cpp"
"${CMAKE_CURRENT_LIST_DIR}/TTX.cpp"
"${CMAKE_CURRENT_LIST_DIR}/VBF.cpp"
"${CMAKE_CURRENT_LIST_DIR}/VTF.cpp")

sourcepp_add_tbb(sourcepp_vtfpp)
sourcepp_add_threads(sourcepp_vtfpp)

if(SOURCEPP_VTFPP_BUILD_WITH_COMPRESSONATOR)
target_link_libraries(sourcepp_vtfpp PRIVATE CMP_Compressonator)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_BUILD_WITH_COMPRESSONATOR)
endif()
if(SOURCEPP_VTFPP_SUPPORT_EXR)
target_link_libraries(sourcepp_vtfpp PRIVATE tinyexr)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_EXR)
endif()
if(SOURCEPP_VTFPP_SUPPORT_JXL)
target_link_libraries(sourcepp_vtfpp PRIVATE jxl)
if(SOURCEPP_BUILD_WITH_THREADS)
target_link_libraries(sourcepp_vtfpp PRIVATE jxl_threads)
endif()
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_JXL)
endif()
if(SOURCEPP_VTFPP_SUPPORT_QOI)
target_link_libraries(sourcepp_vtfpp PRIVATE qoi)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_QOI)
endif()
if(SOURCEPP_VTFPP_SUPPORT_WEBP)
target_link_libraries(sourcepp_vtfpp PRIVATE webp)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_WEBP)
endif()
add_pretty_parser(vtfpp
DEPS bcdec libzstd_static miniz sourcepp_compression sourcepp_parser sourcepp_stb
PRECOMPILED_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/DDS.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/DistanceMapping.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/HOT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageConversion.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageFormats.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImagePixel.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/ImageQuantize.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/PPL.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/PSFrames.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/SHT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/TTX.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/VBF.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/VTF.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/vtfpp/vtfpp.h"
SOURCES
"${CMAKE_CURRENT_LIST_DIR}/DDS.cpp"
"${CMAKE_CURRENT_LIST_DIR}/DistanceMapping.cpp"
"${CMAKE_CURRENT_LIST_DIR}/HOT.cpp"
"${CMAKE_CURRENT_LIST_DIR}/ImageConversion.cpp"
"${CMAKE_CURRENT_LIST_DIR}/ImageQuantize.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PPL.cpp"
"${CMAKE_CURRENT_LIST_DIR}/PSFrames.cpp"
"${CMAKE_CURRENT_LIST_DIR}/SHT.cpp"
"${CMAKE_CURRENT_LIST_DIR}/TTX.cpp"
"${CMAKE_CURRENT_LIST_DIR}/VBF.cpp"
"${CMAKE_CURRENT_LIST_DIR}/VTF.cpp")

sourcepp_add_tbb(sourcepp_vtfpp)
sourcepp_add_threads(sourcepp_vtfpp)

if(SOURCEPP_VTFPP_BUILD_WITH_COMPRESSONATOR)
target_link_libraries(sourcepp_vtfpp PRIVATE CMP_Compressonator)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_BUILD_WITH_COMPRESSONATOR)
endif()
if(SOURCEPP_VTFPP_SUPPORT_EXR)
target_link_libraries(sourcepp_vtfpp PRIVATE tinyexr)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_EXR)
endif()
if(SOURCEPP_VTFPP_SUPPORT_JXL)
target_link_libraries(sourcepp_vtfpp PRIVATE jxl)
if(SOURCEPP_BUILD_WITH_THREADS)
target_link_libraries(sourcepp_vtfpp PRIVATE jxl_threads)
endif()
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_JXL)
endif()
if(SOURCEPP_VTFPP_SUPPORT_QOI)
target_link_libraries(sourcepp_vtfpp PRIVATE qoi)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_QOI)
endif()
if(SOURCEPP_VTFPP_SUPPORT_WEBP)
target_link_libraries(sourcepp_vtfpp PRIVATE webp)
target_compile_definitions(sourcepp_vtfpp PUBLIC VTFPP_SUPPORT_WEBP)
endif()
Loading