Skip to content
This repository was archived by the owner on Jun 22, 2025. It is now read-only.
Merged
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
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ target_link_libraries(vtex2 PRIVATE vtflib_static com fmt::fmt)
target_include_directories(vtex2 PRIVATE src external)
target_include_directories(com PRIVATE src external external/vtflib/lib)

if (UNIX)
# Don't build vtex2 cli as a dynamic executable
set_target_properties(vtex2 PROPERTIES LINK_FLAGS "-static-libgcc -static-libstdc++ -static")
endif()

if (BUILD_GUI)
target_link_libraries(vtfview PRIVATE vtflib_static com fmt::fmt)
target_include_directories(vtfview PRIVATE src external)
Expand Down
22 changes: 21 additions & 1 deletion src/cli/action_convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace opts
static int nomips;
static int toDX;
static int quiet;
static int swizzle;
} // namespace opts

static bool get_version_from_str(const std::string& str, int& major, int& minor);
Expand Down Expand Up @@ -236,6 +237,13 @@ const OptionList& ActionConvert::get_options() const {
.type(OptType::Bool)
.help("Silence output messages that aren't errors")
);

opts::swizzle = opts.add(
ActionOption()
.long_opt("--swizzle")
.type(OptType::String)
.help("Perform an in-place swizzle on the image data")
);
};
return opts;
}
Expand Down Expand Up @@ -371,12 +379,24 @@ bool ActionConvert::process_file(
auto image = std::make_shared<imglib::Image>(
vtfFile->GetData(0, 0, 0, 0), procChanType, 4, vtfFile->GetWidth(), vtfFile->GetHeight(), true);
if (!image->process(imglib::PROC_GL_TO_DX_NORM)) {

std::cerr << "Could not process vtf\n";
return false;
}
}

// Swizzle the image if requested
if (opts.has(opts::swizzle)) {
uint32_t mask = imglib::swizzle_from_str(opts.get<std::string>(opts::swizzle).data());
if (mask != lwiconv::NO_SWIZZLE) {
auto image = std::make_shared<imglib::Image>(
vtfFile->GetData(0, 0, 0, 0), procChanType, 4, vtfFile->GetWidth(), vtfFile->GetHeight(), true);
if (!image->swizzle(mask)) {
std::cerr << "Could not swizzle vtf\n";
return false;
}
}
}

// Set the properties based on user input
if (!set_properties(vtfFile.get())) {
std::cerr << "Could not set properties on VTF\n";
Expand Down
32 changes: 32 additions & 0 deletions src/common/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ static bool process_image_internal(void* indata, int comps, int w, int h, ProcFl
T* cur = data + i;
if (flags & PROC_GL_TO_DX_NORM)
cur[1] = FULL_VAL<T> - cur[1]; // Invert green channel
if (flags & PROC_INVERT_ALPHA)
cur[3] = FULL_VAL<T> - cur[3];
}
return true;
}
Expand Down Expand Up @@ -401,3 +403,33 @@ size_t imglib::channel_size(ChannelType type) {
return 1;
}
}

bool Image::swizzle(uint32_t mask) {
switch (m_type) {
case ChannelType::UInt8:
return lwiconv::swizzle(static_cast<uint8_t*>(m_data), m_width, m_height, m_comps, mask);
case ChannelType::UInt16:
return lwiconv::swizzle(static_cast<uint16_t*>(m_data), m_width, m_height, m_comps, mask);
case ChannelType::Float:
return lwiconv::swizzle(static_cast<float*>(m_data), m_width, m_height, m_comps, mask);
default:
return false;
}
}

uint32_t imglib::swizzle_from_str(const char* str) {
uint32_t mask = 0;
for (int i = 0; i < lwiconv::MAX_CHANNELS; ++i, ++str) {
if (!*str) break;
int v = 0;
switch (*str) {
case 'r': v = 0; break;
case 'g': v = 1; break;
case 'b': v = 2; break;
case 'a': v = 3; break;
default: return 0xFFFFFFFF;
}
mask |= v << ((lwiconv::MAX_CHANNELS-i-1)*8);
}
return mask;
}
11 changes: 11 additions & 0 deletions src/common/image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace imglib
{

constexpr int MAX_CHANNELS = 4;

using lwiconv::make_swizzle;

/**
* Per-channel data type
Expand Down Expand Up @@ -48,6 +50,9 @@ namespace imglib

using ProcFlags = uint32_t;
inline constexpr ProcFlags PROC_GL_TO_DX_NORM = (1 << 0);
inline constexpr ProcFlags PROC_INVERT_ALPHA = (1 << 1);

uint32_t swizzle_from_str(const char* str);

/**
* Returns the number of bytes per pixel for the format
Expand Down Expand Up @@ -124,6 +129,12 @@ namespace imglib
* @param pdef Default pixel fill for uninitialized pixels
*/
bool convert(ChannelType type, int channels = -1, const lwiconv::PixelF& pdef = {0,0,0,1});

/**
* Perform in-place swizzle of components
* @param mask Swizzle mask. @see lwiconv::make_swizzle
*/
bool swizzle(uint32_t mask);

/**
* Returns the VTF format which matches up to the data we have internally here
Expand Down
60 changes: 60 additions & 0 deletions src/common/lwiconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <cstdint>
#include <cstddef>
#include <cassert>

namespace lwiconv
{
Expand Down Expand Up @@ -90,6 +91,7 @@ inline void pixel_to_data(T* pout, const PixelF& p) {
/**
* \brief Convert buffer from one color format to another
* The input and output buffers are assumed to be the same dimensions.
* in and out must not overlap.
* \param in Pointer to the input buffer
* \param out Pointer to the output buffer
* \param w Width of the image
Expand Down Expand Up @@ -138,4 +140,62 @@ static void convert_generic(const void* in, void* out, int w, int h, int inC, in
outConv(pout, inConv(pin, channelDefaults));
}

constexpr uint32_t NO_SWIZZLE = 0x00010203;

/**
* \brief Makes a 32-bit swizzle mask thingy
* Parameters A B C D indicate where the channel should get its data.
* So, make_swizzle(3, 2, 1, 0) would turn an RGBA image into ABGR
*/
static inline constexpr uint32_t make_swizzle(int a, int b, int c, int d) {
return (a & 0xFF) << 24 | (b & 0xFF) << 16 | (c & 0xFF) << 8 | (d & 0xFF);
}

namespace detail {

static inline PixelF swizzle_one(const PixelF& pixel, uint32_t mask) {
return { pixel.d[(mask >> 24) & 0xFF], pixel.d[(mask >> 16) & 0xFF], pixel.d[(mask >> 8) & 0xFF], pixel.d[mask & 0xFF] };
}

}

template<typename T>
static bool swizzle(T* image, int w, int h, int comps, uint32_t swizzle) {
assert(comps <= MAX_CHANNELS && comps > 0);
if (comps <= 0 || comps > MAX_CHANNELS)
return false;

// Check that swizzle is in bounds too
for (int i = 0; i < comps; ++i)
if (((swizzle >> (i*8)) & 0xFF) > comps)
return false;

using fnInConv = PixelF (*)(const T*, const PixelF&);
constexpr fnInConv inConvFuncs[MAX_CHANNELS] = {
detail::pixel_from_data<T, 1>,
detail::pixel_from_data<T, 2>,
detail::pixel_from_data<T, 3>,
detail::pixel_from_data<T, 4>,
};
const fnInConv inConv = inConvFuncs[comps-1];

using fnOutConv = void (*)(T*, const PixelF&);
constexpr fnOutConv outConvFuncs[MAX_CHANNELS] = {
detail::pixel_to_data<T, 1>,
detail::pixel_to_data<T, 2>,
detail::pixel_to_data<T, 3>,
detail::pixel_to_data<T, 4>,
};
const fnOutConv outConv = outConvFuncs[comps-1];

const size_t stride = comps * sizeof(T);
for (int m = 0; m < w*h; ++m, image += stride) {
outConv(image, detail::swizzle_one(
inConv(image, {0,0,0,0}), swizzle
));
}

return true;
}

} // lwiconv
9 changes: 9 additions & 0 deletions src/tests/image_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,12 @@ TEST(ImageTests, Basic8To8)
runTest<uint8_t, uint8_t>(32, 32, 4, 4, {128, 0, 0xFF, 99}, {128, 0, 0xFF, 99});
}

TEST(ImageTests, BasicSwizzle)
{
uint8_t img[4] = {1,2,3,4};
ASSERT_TRUE(swizzle<uint8_t>(img, 1, 1, 4, make_swizzle(3, 2, 1, 0)));
ASSERT_EQ(img[0], 4);
ASSERT_EQ(img[1], 3);
ASSERT_EQ(img[2], 2);
ASSERT_EQ(img[3], 1);
}