Skip to content
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@ These are only the tools and games using `sourcepp` that I know of. If you would
- `vtfpp`
- NICE/Lanczos-3 resize filter's initial implementation was contributed by [@koerismo](https://github.com/koerismo).
- Big-endian support is based on work by [@partyvan](https://github.com/partyvan).
- Distance to alpha support was contributed by [@partyvan](https://github.com/partyvan).
- SHT parser/writer was contributed by [@Trico Everfire](https://github.com/Trico-Everfire).
- Initial VTF write support was loosely based on work by [@Trico Everfire](https://github.com/Trico-Everfire).
- HDRI to cubemap conversion code is modified from the [HdriToCubemap](https://github.com/ivarout/HdriToCubemap) library by [@ivarout](https://github.com/ivarout).
1 change: 1 addition & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ <h2>Special Thanks</h2>
<ul>
<li>NICE/Lanczos-3 resize filter's initial implementation was contributed by <a href="https://github.com/koerismo" target="_blank" rel="noreferrer">@koerismo</a>.</li>
<li>Big-endian support is based on work by <a href="https://github.com/partyvan" target="_blank" rel="noreferrer">@partyvan</a>.</li>
<li>Distance to alpha support was contributed by <a href="https://github.com/partyvan" target="_blank" rel="noreferrer">@partyvan</a>.</li>
<li>SHT parser/writer was contributed by <a href="https://github.com/Trico-Everfire" target="_blank" rel="noreferrer">@Trico Everfire</a>.</li>
<li>Initial VTF write support was loosely based on work by <a href="https://github.com/Trico-Everfire" target="_blank" rel="noreferrer">@Trico Everfire</a>.</li>
<li>HDRI to cubemap conversion code is modified from the <a href="https://github.com/ivarout/HdriToCubemap" target="_blank" rel="noreferrer">HdriToCubemap</a> library by <a href="https://github.com/ivarout" target="_blank" rel="noreferrer">@ivarout</a>.</li>
Expand Down
55 changes: 55 additions & 0 deletions include/vtfpp/DistanceMapping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include <vector>

#include <sourcepp/Macros.h>
#include <vtfpp/ImageConversion.h>

namespace vtfpp::DistanceMapping {

enum class Dither {
NONE = 0, ///< No dithering.
GRADIENT_TANGENT, ///< Experimental dithering approach that diffuses quantization error perpendicular to the distance gradient at any destination pixel. Slow and requires up to double the memory.
// anything generic that doesn't depend on the geometry of the distance field (floyd-steinberg etc) would belong somewhere more general.
};

enum class Flags : uint32_t {
NONE = 0,
DISTANCEAA = 1 << 0, ///< Experimental; interpret the alpha channel as antialiased. Can result in a more precise distance map, but produces nonsense if large gradients are present.
EUCLIDEAN = 1 << 1, ///< The distance-mapping algorithm is a brute-force scan of a *square* area. If this is enabled, only accept distance hits in a circular area.
SAMPLECENTERED = 1 << 2, ///< Search from the center of pixels (in destination coordinate space) rather than in their north-west corners. Can mitigate a perceived southeast shift at extreme reductions.
};
SOURCEPP_BITFLAGS_ENUM(Flags)

/// In one operation, convert an image's alpha channel, or, for single-channel formats, its only channel, to a VTEX-style distance map, and downscale other channels, if present in the output, according to the given resize parameters.
/// @return Empty if:
/// * \p inFormat or \p outFormat do not satisfy their documented predicates,
/// * \p reduceX or \p reduceY are not powers of two,
/// * \p distanceSpread would result in a search radius of zero with either \p reduceX or \p reduceY,
/// * \p alphaThreshold is out of range, or
/// * \p edge is ResizeEdge::ZERO.
///
/// Otherwise, an image at \p width / \p reduceX by \p height / \p reduceY in \p outFormat:
/// * if \p outFormat is single-channel, R contains the distance map.
/// * if \p outFormat has alpha, A contains the distance map, while R/G/B contain:
/// * if \p inFormat had R/G/B, the scaled R/G/B of the input image, otherwise
/// * 0 in all other channels.
[[nodiscard]] std::vector<std::byte> alphaToDistance(
std::span<const std::byte> imageData,
ImageFormat inFormat, ///< Any format that is either single-channel, or has an alpha channel.
ImageFormat outFormat, ///< The same requirements as inFormat; channel count does not need to correspond to inFormat.
uint16_t width,
uint16_t height,
uint16_t reduceX, ///< Power-of-two horizontal reduction factor.
uint16_t reduceY, ///< Power-of-two vertical reduction factor.
bool srgb, ///< Used in the case of RGBA->RGBA. premultipliedAlpha is omitted.
float distanceSpread = 1.f, ///< Scale factor beyond \p reduceX and \p reduceY, of search radius for distance hits.
float alphaThreshold = 0.04f, ///< Threshold below which alpha values are considered zero.
Flags flags = Flags::NONE, ///< Additional behaviors that deviate from VTEX but may be useful; see DistanceFlags.
Dither dither = Dither::NONE, ///< Internally, distance maps are always computed in floating point. When set to a value other than NONE, and the output format is of integral type, dithering is applied prior to quantization. Depending on the application and contents, this can improve or worsen the quality of the distance map.
ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::NICE, ///< Default value mimics VTEX. Does not affect the alpha channel; distance mapping is a distinct sampling operation from source to destination space. Entirely unused if input is single-channel.
ImageConversion::ResizeEdge edge = ImageConversion::ResizeEdge::CLAMP, ///< Dictates the sampling policy of the distance function regardless of whether there are non-alpha channels to be resized.
bool* valveQuirks = nullptr ///< When non-null, mimic VTEX's policy of blanking out any edge pixels in the distance map to 0 (i.e. infinite distance), and report back whether this resulted in any change (such that a command-line tool emulating VTEX would want to report a warning).
);

} // namespace vtfpp::DistanceMapping
1 change: 1 addition & 0 deletions include/vtfpp/vtfpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* include it the same way as any of the other SourcePP libraries.
*/

#include "DistanceMapping.h"
#include "HOT.h"
#include "ImageConversion.h"
#include "ImageFormats.h"
Expand Down
56 changes: 56 additions & 0 deletions lang/c/include/vtfppc/DistanceMapping.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include <sourceppc/Buffer.h>

#include "API.h"
#include "ImageConversion.h"

VTFPP_EXTERN typedef enum {
VTFPP_DISTANCE_MAPPING_DITHER_NONE = 0,
VTFPP_DISTANCE_MAPPING_DITHER_GRADIENT_TANGENT,
} vtfpp_distance_mapping_dither_e;

VTFPP_EXTERN typedef enum {
VTFPP_DISTANCE_MAPPING_FLAG_NONE = 0,
VTFPP_DISTANCE_MAPPING_FLAG_DISTANCEAA = 1 << 0,
VTFPP_DISTANCE_MAPPING_FLAG_EUCLIDEAN = 1 << 1,
VTFPP_DISTANCE_MAPPING_FLAG_SAMPLECENTERED = 1 << 2,
} vtfpp_distance_mapping_flags_e;

VTFPP_API sourcepp_buffer_t vtfpp_distance_mapping_alpha_to_distance(const unsigned char* buffer, size_t bufferLen, vtfpp_image_format_e inFormat, vtfpp_image_format_e outFormat, uint16_t width, uint16_t height, uint16_t reduceX, uint16_t reduceY, int srgb); // REQUIRES MANUAL FREE: sourcepp_buffer_free
VTFPP_API sourcepp_buffer_t vtfpp_distance_mapping_alpha_to_distance_ex(const unsigned char* buffer, size_t bufferLen, vtfpp_image_format_e inFormat, vtfpp_image_format_e outFormat, uint16_t width, uint16_t height, uint16_t reduceX, uint16_t reduceY, int srgb, float distanceSpread, float alphaThreshold, vtfpp_distance_mapping_flags_e flags, vtfpp_distance_mapping_dither_e dither, vtfpp_image_conversion_resize_filter_e filter, vtfpp_image_conversion_resize_edge_e edge, int* valveQuirks); // REQUIRES MANUAL FREE: sourcepp_buffer_free

// C++ conversion routines
#ifdef __cplusplus

#include <vtfpp/DistanceMapping.h>

namespace sourceppc::convert {

inline vtfpp::DistanceMapping::Dither cast(vtfpp_distance_mapping_dither_e value) {
switch (value) {
case VTFPP_DISTANCE_MAPPING_DITHER_NONE: return vtfpp::DistanceMapping::Dither::NONE;
case VTFPP_DISTANCE_MAPPING_DITHER_GRADIENT_TANGENT: return vtfpp::DistanceMapping::Dither::GRADIENT_TANGENT;
}
return vtfpp::DistanceMapping::Dither::NONE;
}

inline vtfpp_distance_mapping_dither_e cast(vtfpp::DistanceMapping::Dither value) {
switch (value) {
case vtfpp::DistanceMapping::Dither::NONE: return VTFPP_DISTANCE_MAPPING_DITHER_NONE;
case vtfpp::DistanceMapping::Dither::GRADIENT_TANGENT: return VTFPP_DISTANCE_MAPPING_DITHER_GRADIENT_TANGENT;
}
return VTFPP_DISTANCE_MAPPING_DITHER_NONE;
}

inline vtfpp::DistanceMapping::Flags cast(vtfpp_distance_mapping_flags_e flags) {
return static_cast<vtfpp::DistanceMapping::Flags>(flags);
}

inline vtfpp_distance_mapping_flags_e cast(vtfpp::DistanceMapping::Flags flags) {
return static_cast<vtfpp_distance_mapping_flags_e>(flags);
}

} // namespace sourceppc::convert

#endif
1 change: 1 addition & 0 deletions lang/c/include/vtfppc/vtfpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* include it the same way as any of the other SourcePP libraries.
*/

#include "DistanceMapping.h"
#include "HOT.h"
#include "ImageConversion.h"
#include "ImageFormats.h"
Expand Down
1 change: 1 addition & 0 deletions lang/c/src/gameppc/_gameppc.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_pretty_parser(gamepp C
SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/gameppc/API.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/gameppc/gamepp.h"
"${CMAKE_CURRENT_LIST_DIR}/gamepp.cpp")
1 change: 1 addition & 0 deletions lang/c/src/steamppc/_steamppc.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_pretty_parser(steampp C
SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/steamppc/API.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/steamppc/steampp.h"
"${CMAKE_CURRENT_LIST_DIR}/steampp.cpp")
1 change: 1 addition & 0 deletions lang/c/src/vcryptppc/_vcryptppc.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_pretty_parser(vcryptpp C
SOURCES
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vcryptppc/API.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vcryptppc/vcryptpp.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vcryptppc/VFONT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vcryptppc/VICE.h"
Expand Down
1 change: 1 addition & 0 deletions lang/c/src/vpkppc/_vpkppc.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_pretty_parser(vpkpp C
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/WAD3.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/XZP.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/format/ZIP.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/API.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/Attribute.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/Entry.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vpkppc/Options.h"
Expand Down
26 changes: 26 additions & 0 deletions lang/c/src/vtfppc/DistanceMapping.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <vtfppc/DistanceMapping.h>

#include <sourceppc/Helpers.h>

using namespace sourceppc;
using namespace vtfpp;

VTFPP_API sourcepp_buffer_t vtfpp_distance_mapping_alpha_to_distance(const unsigned char* buffer, size_t bufferLen, vtfpp_image_format_e inFormat, vtfpp_image_format_e outFormat, uint16_t width, uint16_t height, uint16_t reduceX, uint16_t reduceY, int srgb) {
SOURCEPP_EARLY_RETURN_VAL(buffer, SOURCEPP_BUFFER_INVALID);
SOURCEPP_EARLY_RETURN_VAL(bufferLen, SOURCEPP_BUFFER_INVALID);

return convert::toBuffer(DistanceMapping::alphaToDistance({reinterpret_cast<const std::byte*>(buffer), bufferLen}, convert::cast(inFormat), convert::cast(outFormat), width, height, reduceX, reduceY, srgb));
}

VTFPP_API sourcepp_buffer_t vtfpp_distance_mapping_alpha_to_distance_ex(const unsigned char* buffer, size_t bufferLen, vtfpp_image_format_e inFormat, vtfpp_image_format_e outFormat, uint16_t width, uint16_t height, uint16_t reduceX, uint16_t reduceY, int srgb, float distanceSpread, float alphaThreshold, vtfpp_distance_mapping_flags_e flags, vtfpp_distance_mapping_dither_e dither, vtfpp_image_conversion_resize_filter_e filter, vtfpp_image_conversion_resize_edge_e edge, int* valveQuirks) {
SOURCEPP_EARLY_RETURN_VAL(buffer, SOURCEPP_BUFFER_INVALID);
SOURCEPP_EARLY_RETURN_VAL(bufferLen, SOURCEPP_BUFFER_INVALID);

if (!valveQuirks) {
return convert::toBuffer(DistanceMapping::alphaToDistance({reinterpret_cast<const std::byte*>(buffer), bufferLen}, convert::cast(inFormat), convert::cast(outFormat), width, height, reduceX, reduceY, srgb, distanceSpread, alphaThreshold, convert::cast(flags), convert::cast(dither), convert::cast(filter), convert::cast(edge), nullptr));
}
bool valveQuirksBool = *valveQuirks;
const auto out = convert::toBuffer(DistanceMapping::alphaToDistance({reinterpret_cast<const std::byte*>(buffer), bufferLen}, convert::cast(inFormat), convert::cast(outFormat), width, height, reduceX, reduceY, srgb, distanceSpread, alphaThreshold, convert::cast(flags), convert::cast(dither), convert::cast(filter), convert::cast(edge), &valveQuirksBool));
*valveQuirks = valveQuirksBool;
return out;
}
3 changes: 3 additions & 0 deletions lang/c/src/vtfppc/_vtfppc.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
add_pretty_parser(vtfpp C
PRECOMPILED_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/API.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/DistanceMapping.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/HOT.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/ImageConversion.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/ImageFormats.h"
Expand All @@ -12,6 +14,7 @@ add_pretty_parser(vtfpp C
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/VTF.h"
"${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/vtfppc/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}/ImageFormats.cpp"
Expand Down
6 changes: 6 additions & 0 deletions lang/csharp/src/vtfpp/DLL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ internal static partial class DLL
{
private const string Name = "sourcepp_vtfppc";

[LibraryImport(Name)]
public static partial sourcepp.DLL.Buffer vtfpp_distance_mapping_alpha_to_distance(ReadOnlySpan<byte> buffer, ulong bufferLen, ImageFormat inFormat, ImageFormat outFormat, ushort width, ushort height, ushort reduceX, ushort reduceY, int srgb);

[LibraryImport(Name)]
public static partial sourcepp.DLL.Buffer vtfpp_distance_mapping_alpha_to_distance_ex(ReadOnlySpan<byte> buffer, ulong bufferLen, ImageFormat inFormat, ImageFormat outFormat, ushort width, ushort height, ushort reduceX, ushort reduceY, int srgb, float distanceSpread, float alphaThreshold, DistanceMapping.Flags flags, DistanceMapping.Dither dither, ImageConversion.ResizeFilter filter, ImageConversion.ResizeEdge edge, nint valveQuirks);

[LibraryImport(Name)]
public static partial nint vtfpp_hot_create();

Expand Down
32 changes: 32 additions & 0 deletions lang/csharp/src/vtfpp/DistanceMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace sourcepp.vtfpp;

public static class DistanceMapping
{
public enum Dither
{
NONE = 0,
GRADIENT_TANGENT,
}

public enum Flags : uint
{
NONE = 0,
DISTANCEAA = 1 << 0,
EUCLIDEAN = 1 << 1,
SAMPLECENTERED = 1 << 2,
}

public static byte[] AlphaToDistance(ReadOnlySpan<byte> buffer, ImageFormat inFormat, ImageFormat outFormat, ushort width, ushort height, ushort resizeX, ushort resizeY, bool srgb, ref bool valveQuirks, float distanceSpread = 1.0f, float alphaThreshold = 0.04f, Flags flags = Flags.NONE, Dither dither = Dither.NONE, ImageConversion.ResizeFilter filter = ImageConversion.ResizeFilter.NICE, ImageConversion.ResizeEdge edge = ImageConversion.ResizeEdge.CLAMP)
{
var valveQuirksInt = Convert.ToInt32(valveQuirks);
unsafe
{
var valveQuirksIntPtr = &valveQuirksInt;
var output = new sourcepp.Buffer(DLL.vtfpp_distance_mapping_alpha_to_distance_ex(buffer, (ulong)buffer.Length, inFormat, outFormat, width, height, resizeX, resizeY, Convert.ToInt32(srgb), distanceSpread, alphaThreshold, flags, dither, filter, edge, valveQuirks ? (nint)valveQuirksIntPtr : 0)).Read<byte>();
valveQuirks = Convert.ToBoolean(valveQuirksInt);
return output;
}
}
}
2 changes: 1 addition & 1 deletion lang/csharp/test/vpkpp/PackFileTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void Open()

Assert.IsFalse(vpk.IsReadOnly);

Assert.AreEqual(3509u, vpk.EntryCount());
Assert.AreEqual(3524u, vpk.EntryCount());

Assert.AreEqual(BasePortalPath + "portal_pak_dir.vpk", vpk.Filepath);
Assert.AreEqual(BasePortalPath + "portal_pak", vpk.TruncatedFilepath);
Expand Down
20 changes: 20 additions & 0 deletions lang/python/src/vtfpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,26 @@ inline void register_python(py::module_& m) {
}, "image_data"_a, "format"_a, "width"_a, "height"_a);
}

{
using namespace DistanceMapping;
auto DistanceMapping = vtfpp.def_submodule("DistanceMapping");

py::enum_<Dither>(DistanceMapping, "Dither")
.value("NONE", Dither::NONE)
.value("GRADIENT_TANGENT", Dither::GRADIENT_TANGENT);

py::enum_<Flags>(DistanceMapping, "Flags", py::is_flag())
.value("NONE", Flags::NONE)
.value("DISTANCEAA", Flags::DISTANCEAA)
.value("EUCLIDEAN", Flags::EUCLIDEAN)
.value("SAMPLECENTERED", Flags::SAMPLECENTERED);

DistanceMapping.def("alpha_to_distance", [](const py::bytes& imageData, ImageFormat inFormat, ImageFormat outFormat, uint16_t width, uint16_t height, uint16_t reduceX, uint16_t reduceY, bool srgb, float distanceSpread = 1.f, float alphaThreshold = 0.04f, Flags flags = Flags::NONE, Dither dither = Dither::NONE, ImageConversion::ResizeFilter filter = ImageConversion::ResizeFilter::NICE, ImageConversion::ResizeEdge edge = ImageConversion::ResizeEdge::CLAMP, bool enableValveQuirks = false) {
const auto d = alphaToDistance({static_cast<const std::byte*>(imageData.data()), static_cast<const std::byte*>(imageData.data()) + imageData.size()}, inFormat, outFormat, width, height, reduceX, reduceY, srgb, distanceSpread, alphaThreshold, flags, dither, filter, edge, enableValveQuirks ? &enableValveQuirks : nullptr);
return std::pair{py::bytes{d.data(), d.size()}, enableValveQuirks};
}, "image_data"_a, "in_format"_a, "out_format"_a, "width"_a, "height"_a, "reduce_x"_a, "reduce_y"_a, "srgb"_a, "distance_spread"_a = 1.f, "alpha_threshold"_a = 0.04f, "flags"_a = Flags::NONE, "dither"_a = Dither::NONE, "filter"_a = ImageConversion::ResizeFilter::NICE, "edge"_a = ImageConversion::ResizeEdge::CLAMP, "enable_valve_quirks"_a = false);
}

{
using namespace ImageQuantize;
auto ImageQuantize = vtfpp.def_submodule("ImageQuantize");
Expand Down
Loading
Loading