Compare commits
33 commits
0da27b59be
...
2854aa5104
Author | SHA1 | Date | |
---|---|---|---|
2854aa5104 | |||
f3d1ad425c | |||
7fb458dad5 | |||
5913b7669b | |||
d137dc44ff | |||
045d3ef18d | |||
3a97f14199 | |||
2d47b7f892 | |||
47ba03337b | |||
d4661996d2 | |||
ea66a44238 | |||
16f0e0e865 | |||
03b3c90e9a | |||
6ed76af210 | |||
3b11564a4a | |||
020993fb19 | |||
d29d2434ab | |||
d5d722022c | |||
3b0a5ab7dd | |||
5cee471f1f | |||
7cca2bfbff | |||
a7dc666082 | |||
bc02b924e2 | |||
8c1190d923 | |||
bd5cfaebd3 | |||
6cefcc5f94 | |||
b73c6fb94b | |||
a0de033d34 | |||
38f7248f49 | |||
d0f14585b6 | |||
aaa986b0eb | |||
c823f90c14 | |||
4e4ee7f558 |
10 changed files with 1499 additions and 31 deletions
|
@ -6,12 +6,13 @@ set(blueNoiseGen_SOURCES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/image.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/image.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/arg_parse.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/arg_parse.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/utility.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wall -Wextra -Wpedantic
|
-Wall -Wextra -Wpedantic
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:-Weffc++>
|
$<$<COMPILE_LANGUAGE:CXX>:-Weffc++>
|
||||||
$<$<CONFIG:DEBUG>:-O0>
|
$<$<CONFIG:DEBUG>:-Og>
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
|
if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
|
||||||
|
@ -29,29 +30,48 @@ if(NOT DEFINED DISABLE_OPENCL OR NOT DISABLE_OPENCL)
|
||||||
else()
|
else()
|
||||||
message(STATUS "Not checking for OpenCL")
|
message(STATUS "Not checking for OpenCL")
|
||||||
endif()
|
endif()
|
||||||
|
if(NOT DEFINED DISABLE_VULKAN OR NOT DISABLE_VULKAN)
|
||||||
|
find_package(Vulkan)
|
||||||
|
if(NOT DEFINED Vulkan_FOUND)
|
||||||
|
set(DISABLE_VULKAN True)
|
||||||
|
message(WARNING "Vulkan not found, Vulkan usage is disabled.")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "Not checking for Vulkan")
|
||||||
|
endif()
|
||||||
find_package(PNG REQUIRED)
|
find_package(PNG REQUIRED)
|
||||||
|
|
||||||
add_executable(blueNoiseGen ${blueNoiseGen_SOURCES})
|
add_executable(blueNoiseGen ${blueNoiseGen_SOURCES})
|
||||||
target_compile_features(blueNoiseGen PUBLIC cxx_std_17)
|
target_compile_features(blueNoiseGen PUBLIC cxx_std_17)
|
||||||
target_compile_definitions(blueNoiseGen PRIVATE CL_TARGET_OPENCL_VERSION=300)
|
target_compile_definitions(blueNoiseGen PRIVATE CL_TARGET_OPENCL_VERSION=300)
|
||||||
|
target_include_directories(blueNoiseGen PUBLIC Threads::Threads ${PNG_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(blueNoiseGen PUBLIC Threads::Threads ${PNG_LIBRARIES})
|
||||||
|
|
||||||
if(DEFINED DISABLE_OPENCL AND DISABLE_OPENCL)
|
if(DEFINED DISABLE_OPENCL AND DISABLE_OPENCL)
|
||||||
message(STATUS "OpenCL usage is disabled.")
|
message(STATUS "OpenCL usage is disabled.")
|
||||||
target_include_directories(blueNoiseGen PUBLIC
|
|
||||||
Threads::Threads
|
|
||||||
${PNG_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(blueNoiseGen PUBLIC
|
|
||||||
Threads::Threads
|
|
||||||
${PNG_LIBRARIES})
|
|
||||||
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=0)
|
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=0)
|
||||||
else()
|
else()
|
||||||
message(STATUS "OpenCL usage is enabled.")
|
message(STATUS "OpenCL usage is enabled.")
|
||||||
target_include_directories(blueNoiseGen PUBLIC
|
target_include_directories(blueNoiseGen PUBLIC
|
||||||
Threads::Threads
|
${OpenCL_INCLUDE_DIRS})
|
||||||
${OpenCL_INCLUDE_DIRS}
|
|
||||||
${PNG_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(blueNoiseGen PUBLIC
|
target_link_libraries(blueNoiseGen PUBLIC
|
||||||
Threads::Threads
|
${OpenCL_LIBRARIES})
|
||||||
${OpenCL_LIBRARIES}
|
|
||||||
${PNG_LIBRARIES})
|
|
||||||
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=1)
|
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_OPENCL_ENABLED=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(DEFINED DISABLE_VULKAN AND DISABLE_VULKAN)
|
||||||
|
message(STATUS "Vulkan usage is disabled.")
|
||||||
|
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_VULKAN_ENABLED=0)
|
||||||
|
else()
|
||||||
|
message(STATUS "Vulkan usage is enabled.")
|
||||||
|
target_include_directories(blueNoiseGen PUBLIC
|
||||||
|
${Vulkan_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(blueNoiseGen PUBLIC
|
||||||
|
${Vulkan_LIBRARIES})
|
||||||
|
target_compile_definitions(blueNoiseGen PRIVATE DITHERING_VULKAN_ENABLED=1)
|
||||||
|
if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||||
|
target_compile_definitions(blueNoiseGen PRIVATE VULKAN_VALIDATION=1)
|
||||||
|
else()
|
||||||
|
target_compile_definitions(blueNoiseGen PRIVATE VULKAN_VALIDATION=0)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021,2023 Stephen Seo
|
Copyright (c) 2021,2023-2024 Stephen Seo
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -7,6 +7,7 @@ Args::Args()
|
||||||
: generate_blue_noise_(false),
|
: generate_blue_noise_(false),
|
||||||
use_opencl_(true),
|
use_opencl_(true),
|
||||||
overwrite_file_(false),
|
overwrite_file_(false),
|
||||||
|
use_vulkan_(true),
|
||||||
blue_noise_size_(32),
|
blue_noise_size_(32),
|
||||||
threads_(4),
|
threads_(4),
|
||||||
output_filename_("output.png") {}
|
output_filename_("output.png") {}
|
||||||
|
@ -23,10 +24,12 @@ void Args::DisplayHelp() {
|
||||||
" -t <int> | --threads <int>\t\tUse CPU thread count when "
|
" -t <int> | --threads <int>\t\tUse CPU thread count when "
|
||||||
"not using "
|
"not using "
|
||||||
"OpenCL\n"
|
"OpenCL\n"
|
||||||
" -o <filelname> | --output <filename>\tOutput filename to "
|
" -o <filename> | --output <filename>\tOutput filename to "
|
||||||
"use\n"
|
"use\n"
|
||||||
" --overwrite\t\t\t\tEnable overwriting of file (default "
|
" --overwrite\t\t\t\tEnable overwriting of file (default "
|
||||||
"disabled)\n";
|
"disabled)\n"
|
||||||
|
" --usevulkan | --nousevulkan\t\t\tUse/Disable Vulkan (enabled "
|
||||||
|
"by default)\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Args::ParseArgs(int argc, char **argv) {
|
bool Args::ParseArgs(int argc, char **argv) {
|
||||||
|
@ -69,6 +72,10 @@ bool Args::ParseArgs(int argc, char **argv) {
|
||||||
output_filename_ = std::string(argv[1]);
|
output_filename_ = std::string(argv[1]);
|
||||||
--argc;
|
--argc;
|
||||||
++argv;
|
++argv;
|
||||||
|
} else if (std::strcmp(argv[0], "--usevulkan") == 0) {
|
||||||
|
use_vulkan_ = true;
|
||||||
|
} else if (std::strcmp(argv[0], "--nousevulkan") == 0) {
|
||||||
|
use_vulkan_ = false;
|
||||||
} else {
|
} else {
|
||||||
std::cout << "WARNING: Ignoring invalid input \"" << argv[0] << "\""
|
std::cout << "WARNING: Ignoring invalid input \"" << argv[0] << "\""
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
|
@ -14,6 +14,7 @@ struct Args {
|
||||||
bool generate_blue_noise_;
|
bool generate_blue_noise_;
|
||||||
bool use_opencl_;
|
bool use_opencl_;
|
||||||
bool overwrite_file_;
|
bool overwrite_file_;
|
||||||
|
bool use_vulkan_;
|
||||||
unsigned int blue_noise_size_;
|
unsigned int blue_noise_size_;
|
||||||
unsigned int threads_;
|
unsigned int threads_;
|
||||||
std::string output_filename_;
|
std::string output_filename_;
|
||||||
|
|
1208
src/blue_noise.cpp
1208
src/blue_noise.cpp
File diff suppressed because it is too large
Load diff
50
src/blue_noise.glsl
Normal file
50
src/blue_noise.glsl
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
int twoToOne(int x, int y, int width, int height) {
|
||||||
|
while (x < 0) {
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
while (y < 0) {
|
||||||
|
y += height;
|
||||||
|
}
|
||||||
|
x = x % width;
|
||||||
|
y = y % height;
|
||||||
|
return x + y * width;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout(binding = 0) readonly buffer PreComputed { float precomputed[]; };
|
||||||
|
|
||||||
|
layout(binding = 1) writeonly buffer FilterOut { float filter_out[]; };
|
||||||
|
|
||||||
|
layout(binding = 2) readonly buffer PBP { int pbp[]; };
|
||||||
|
|
||||||
|
layout(binding = 3) readonly buffer Other {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int filter_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(local_size_x = 256) in;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint index = gl_GlobalInvocationID.x;
|
||||||
|
if (index >= width * height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = int(index % width);
|
||||||
|
int y = int(index / width);
|
||||||
|
|
||||||
|
float sum = 0.0F;
|
||||||
|
for (int q = 0; q < filter_size; ++q) {
|
||||||
|
int q_prime = height - filter_size / 2 + y + q;
|
||||||
|
for (int p = 0; p < filter_size; ++p) {
|
||||||
|
int p_prime = width - filter_size / 2 + x + p;
|
||||||
|
if (pbp[twoToOne(p_prime, q_prime, width, height)] != 0) {
|
||||||
|
sum += precomputed[twoToOne(p, q, filter_size, filter_size)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_out[index] = sum;
|
||||||
|
}
|
|
@ -4,7 +4,9 @@
|
||||||
#if DITHERING_OPENCL_ENABLED == 1
|
#if DITHERING_OPENCL_ENABLED == 1
|
||||||
#include <CL/opencl.h>
|
#include <CL/opencl.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/sysinfo.h>
|
#if DITHERING_VULKAN_ENABLED == 1
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -28,11 +30,123 @@
|
||||||
namespace dither {
|
namespace dither {
|
||||||
|
|
||||||
image::Bl blue_noise(int width, int height, int threads = 1,
|
image::Bl blue_noise(int width, int height, int threads = 1,
|
||||||
bool use_opencl = true);
|
bool use_opencl = true, bool use_vulkan = true);
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
std::vector<unsigned int> blue_noise_impl(int width, int height,
|
std::vector<unsigned int> blue_noise_impl(int width, int height,
|
||||||
int threads = 1);
|
int threads = 1);
|
||||||
|
|
||||||
|
#if DITHERING_VULKAN_ENABLED == 1
|
||||||
|
struct QueueFamilyIndices {
|
||||||
|
QueueFamilyIndices();
|
||||||
|
|
||||||
|
std::optional<uint32_t> computeFamily;
|
||||||
|
|
||||||
|
bool isComplete();
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueFamilyIndices vulkan_find_queue_families(VkPhysicalDevice device);
|
||||||
|
|
||||||
|
std::optional<uint32_t> vulkan_find_memory_type(VkPhysicalDevice phys_dev,
|
||||||
|
uint32_t t_filter,
|
||||||
|
VkMemoryPropertyFlags props);
|
||||||
|
|
||||||
|
bool vulkan_create_buffer(VkDevice device, VkPhysicalDevice phys_dev,
|
||||||
|
VkDeviceSize size, VkBufferUsageFlags usage,
|
||||||
|
VkMemoryPropertyFlags props, VkBuffer &buf,
|
||||||
|
VkDeviceMemory &buf_mem);
|
||||||
|
|
||||||
|
void vulkan_copy_buffer(VkDevice device, VkCommandPool command_pool,
|
||||||
|
VkQueue queue, VkBuffer src_buf, VkBuffer dst_buf,
|
||||||
|
VkDeviceSize size);
|
||||||
|
|
||||||
|
void vulkan_flush_buffer(VkDevice device, VkDeviceMemory memory);
|
||||||
|
void vulkan_invalidate_buffer(VkDevice device, VkDeviceMemory memory);
|
||||||
|
|
||||||
|
std::vector<unsigned int> blue_noise_vulkan_impl(
|
||||||
|
VkDevice device, VkPhysicalDevice phys_device,
|
||||||
|
VkCommandBuffer command_buffer, VkCommandPool command_pool, VkQueue queue,
|
||||||
|
VkBuffer pbp_buf, VkPipeline pipeline, VkPipelineLayout pipeline_layout,
|
||||||
|
VkDescriptorSet descriptor_set, VkBuffer filter_out_buf, const int width,
|
||||||
|
const int height);
|
||||||
|
|
||||||
|
std::vector<float> vulkan_buf_to_vec(float *mapped, unsigned int size);
|
||||||
|
|
||||||
|
inline bool vulkan_get_filter(
|
||||||
|
VkDevice device, VkCommandBuffer command_buffer, VkCommandPool command_pool,
|
||||||
|
VkQueue queue, VkBuffer pbp_buf, VkPipeline pipeline,
|
||||||
|
VkPipelineLayout pipeline_layout, VkDescriptorSet descriptor_set,
|
||||||
|
VkBuffer filter_out_buf, const int size, std::vector<bool> &pbp,
|
||||||
|
bool reversed_pbp, const std::size_t global_size, int *pbp_mapped_int,
|
||||||
|
VkBuffer staging_pbp_buffer, VkDeviceMemory staging_pbp_buffer_mem,
|
||||||
|
VkDeviceMemory staging_filter_buffer_mem, VkBuffer staging_filter_buffer) {
|
||||||
|
vkResetCommandBuffer(command_buffer, 0);
|
||||||
|
|
||||||
|
if (reversed_pbp) {
|
||||||
|
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||||
|
pbp_mapped_int[i] = pbp[i] ? 0 : 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||||
|
pbp_mapped_int[i] = pbp[i] ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkan_flush_buffer(device, staging_pbp_buffer_mem);
|
||||||
|
|
||||||
|
// Copy pbp buffer.
|
||||||
|
vulkan_copy_buffer(device, command_pool, queue, staging_pbp_buffer, pbp_buf,
|
||||||
|
size * sizeof(int));
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo begin_info{};
|
||||||
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
|
||||||
|
if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {
|
||||||
|
std::clog << "get_filter ERROR: Failed to begin recording compute "
|
||||||
|
"command buffer!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
|
||||||
|
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||||
|
pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
|
||||||
|
vkCmdDispatch(command_buffer, global_size, 1, 1);
|
||||||
|
if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {
|
||||||
|
std::clog << "get_filter ERROR: Failed to record compute command buffer!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
VkSubmitInfo submit_info{};
|
||||||
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
|
submit_info.commandBufferCount = 1;
|
||||||
|
submit_info.pCommandBuffers = &command_buffer;
|
||||||
|
submit_info.signalSemaphoreCount = 0;
|
||||||
|
submit_info.pSignalSemaphores = nullptr;
|
||||||
|
|
||||||
|
if (vkQueueSubmit(queue, 1, &submit_info, nullptr) != VK_SUCCESS) {
|
||||||
|
std::clog
|
||||||
|
<< "get_filter ERROR: Failed to submit compute command buffer!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vkDeviceWaitIdle(device) != VK_SUCCESS) {
|
||||||
|
std::clog << "get_filter ERROR: Failed to vkDeviceWaitIdle!\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy back filter_out buffer.
|
||||||
|
vulkan_copy_buffer(device, command_pool, queue, filter_out_buf,
|
||||||
|
staging_filter_buffer, size * sizeof(float));
|
||||||
|
|
||||||
|
vulkan_invalidate_buffer(device, staging_filter_buffer_mem);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#if DITHERING_OPENCL_ENABLED == 1
|
#if DITHERING_OPENCL_ENABLED == 1
|
||||||
std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height,
|
std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height,
|
||||||
const int filter_size,
|
const int filter_size,
|
||||||
|
@ -265,6 +379,42 @@ inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
|
||||||
return {min_index, max_index};
|
return {min_index, max_index};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::pair<int, int> filter_minmax_raw_array(const float *const filter,
|
||||||
|
unsigned int size,
|
||||||
|
std::vector<bool> pbp) {
|
||||||
|
// ensure minority pixel is "true"
|
||||||
|
unsigned int count = 0;
|
||||||
|
for (bool value : pbp) {
|
||||||
|
if (value) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count * 2 >= pbp.size()) {
|
||||||
|
// std::cout << "MINMAX flip\n"; // DEBUG
|
||||||
|
for (unsigned int i = 0; i < pbp.size(); ++i) {
|
||||||
|
pbp[i] = !pbp[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float min = std::numeric_limits<float>::infinity();
|
||||||
|
float max = -std::numeric_limits<float>::infinity();
|
||||||
|
int min_index = -1;
|
||||||
|
int max_index = -1;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < size; ++i) {
|
||||||
|
if (!pbp[i] && filter[i] < min) {
|
||||||
|
min_index = i;
|
||||||
|
min = filter[i];
|
||||||
|
}
|
||||||
|
if (pbp[i] && filter[i] > max) {
|
||||||
|
max_index = i;
|
||||||
|
max = filter[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {min_index, max_index};
|
||||||
|
}
|
||||||
|
|
||||||
inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) {
|
inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) {
|
||||||
float min = std::numeric_limits<float>::infinity();
|
float min = std::numeric_limits<float>::infinity();
|
||||||
float max = -std::numeric_limits<float>::infinity();
|
float max = -std::numeric_limits<float>::infinity();
|
||||||
|
|
|
@ -41,7 +41,7 @@ int main(int argc, char **argv) {
|
||||||
std::cout << "Generating blue_noise..." << std::endl;
|
std::cout << "Generating blue_noise..." << std::endl;
|
||||||
image::Bl bl =
|
image::Bl bl =
|
||||||
dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_,
|
dither::blue_noise(args.blue_noise_size_, args.blue_noise_size_,
|
||||||
args.threads_, args.use_opencl_);
|
args.threads_, args.use_opencl_, args.use_vulkan_);
|
||||||
if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_,
|
if (!bl.writeToFile(image::file_type::PNG, args.overwrite_file_,
|
||||||
args.output_filename_)) {
|
args.output_filename_)) {
|
||||||
std::cout << "ERROR: Failed to write blue-noise to file\n";
|
std::cout << "ERROR: Failed to write blue-noise to file\n";
|
||||||
|
|
31
src/utility.cpp
Normal file
31
src/utility.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
utility::Cleanup::Cleanup(std::function<void(void *)> fn, void *ptr)
|
||||||
|
: fn(fn), ptr(ptr) {}
|
||||||
|
|
||||||
|
utility::Cleanup::Cleanup() : fn(), ptr(nullptr) {}
|
||||||
|
|
||||||
|
utility::Cleanup::~Cleanup() {
|
||||||
|
if (this->fn.has_value()) {
|
||||||
|
this->fn.value()(this->ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utility::Cleanup::Cleanup(Cleanup &&other) : fn(other.fn), ptr(other.ptr) {
|
||||||
|
other.fn = std::nullopt;
|
||||||
|
other.ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
utility::Cleanup &utility::Cleanup::operator=(utility::Cleanup &&other) {
|
||||||
|
if (this->fn.has_value()) {
|
||||||
|
this->fn.value()(this->ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->fn = other.fn;
|
||||||
|
this->ptr = other.ptr;
|
||||||
|
|
||||||
|
other.fn = std::nullopt;
|
||||||
|
other.ptr = nullptr;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
|
@ -2,6 +2,8 @@
|
||||||
#define DITHERING_UTILITY_HPP
|
#define DITHERING_UTILITY_HPP
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace utility {
|
namespace utility {
|
||||||
|
@ -28,6 +30,25 @@ inline float dist(int a, int b, int width) {
|
||||||
float dy = axy.second - bxy.second;
|
float dy = axy.second - bxy.second;
|
||||||
return std::sqrt(dx * dx + dy * dy);
|
return std::sqrt(dx * dx + dy * dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Cleanup {
|
||||||
|
public:
|
||||||
|
Cleanup();
|
||||||
|
Cleanup(std::function<void(void *)> fn, void *ptr);
|
||||||
|
~Cleanup();
|
||||||
|
|
||||||
|
// allow move
|
||||||
|
Cleanup(Cleanup &&);
|
||||||
|
Cleanup &operator=(Cleanup &&);
|
||||||
|
|
||||||
|
// deny copy
|
||||||
|
Cleanup(const Cleanup &) = delete;
|
||||||
|
Cleanup &operator=(const Cleanup &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<std::function<void(void *)>> fn;
|
||||||
|
void *ptr;
|
||||||
|
};
|
||||||
} // namespace utility
|
} // namespace utility
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue