diff options
Diffstat (limited to 'extern')
32 files changed, 1541 insertions, 222 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 235c2fa931a..824b2fb0e9c 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -109,3 +109,7 @@ endif() if(WITH_MOD_FLUID) add_subdirectory(mantaflow) endif() + +if (WITH_COMPOSITOR) + add_subdirectory(smaa_areatex) +endif() diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index fe1bf5dc742..1599c03cbad 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC src/devices/NULLDevice.cpp src/devices/ReadDevice.cpp src/devices/SoftwareDevice.cpp + src/devices/ThreadedDevice.cpp src/Exception.cpp src/file/File.cpp src/file/FileManager.cpp @@ -148,6 +149,7 @@ set(PUBLIC_HDR include/devices/NULLDevice.h include/devices/ReadDevice.h include/devices/SoftwareDevice.h + include/devices/ThreadedDevice.h include/Exception.h include/file/File.h include/file/FileManager.h diff --git a/extern/audaspace/include/devices/SoftwareDevice.h b/extern/audaspace/include/devices/SoftwareDevice.h index a350550048b..209be9941b1 100644 --- a/extern/audaspace/include/devices/SoftwareDevice.h +++ b/extern/audaspace/include/devices/SoftwareDevice.h @@ -255,6 +255,7 @@ protected: /** * This function tells the device, to start or pause playback. * \param playing True if device should playback. + * \note This method is only called when the device is locked. */ virtual void playing(bool playing)=0; diff --git a/extern/audaspace/include/devices/ThreadedDevice.h b/extern/audaspace/include/devices/ThreadedDevice.h new file mode 100644 index 00000000000..36c2e68e36f --- /dev/null +++ b/extern/audaspace/include/devices/ThreadedDevice.h @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#pragma once + +/** + * @file ThreadedDevice.h + * @ingroup plugin + * The ThreadedDevice class. + */ + +#include "devices/SoftwareDevice.h" + +#include <thread> + +AUD_NAMESPACE_BEGIN + +/** + * This device extends the SoftwareDevice with code for running mixing in a separate thread. + */ +class AUD_PLUGIN_API ThreadedDevice : public SoftwareDevice +{ +private: + /** + * Whether there is currently playback. + */ + bool m_playing; + + /** + * Whether the current playback should stop. + */ + bool m_stop; + + /** + * The streaming thread. + */ + std::thread m_thread; + + /** + * Starts the streaming thread. + */ + AUD_LOCAL void start(); + + /** + * Streaming thread main function. + */ + AUD_LOCAL virtual void runMixingThread()=0; + + // delete copy constructor and operator= + ThreadedDevice(const ThreadedDevice&) = delete; + ThreadedDevice& operator=(const ThreadedDevice&) = delete; + +protected: + virtual void playing(bool playing); + + /** + * Empty default constructor. To setup the device call the function create() + * and to uninitialize call destroy(). + */ + ThreadedDevice(); + + /** + * Indicates that the mixing thread should be stopped. + * \return Whether the mixing thread should be stopping. + * \warning For thread safety, the device needs to be locked, when this method is called. + */ + inline bool shouldStop() { return m_stop; } + + /** + * This method needs to be called when the mixing thread is stopping. + * \warning For thread safety, the device needs to be locked, when this method is called. + */ + inline void doStop() { m_stop = m_playing = false; } + + /** + * Stops all playback and notifies the mixing thread to stop. + * \warning The device has to be unlocked to not run into a deadlock. + */ + void stopMixingThread(); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp index 2cd3261bd20..10517d1d596 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp @@ -75,6 +75,7 @@ void FFMPEGWriter::encode() m_frame->nb_samples = m_input_samples; m_frame->format = m_codecCtx->sample_fmt; m_frame->channel_layout = m_codecCtx->channel_layout; + m_frame->channels = m_specs.channels; if(avcodec_fill_audio_frame(m_frame, m_specs.channels, m_codecCtx->sample_fmt, reinterpret_cast<data_t*>(data), m_input_buffer.getSize(), 0) < 0) AUD_THROW(FileException, "File couldn't be written, filling the audio frame failed with ffmpeg."); diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index 0a50d5db2c7..3ffe97661d8 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -27,9 +27,9 @@ void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data { PulseAudioDevice* device = (PulseAudioDevice*)data; - device->m_state = AUD_pa_context_get_state(context); + std::lock_guard<ILockable> lock(*device); - AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0); + device->m_state = AUD_pa_context_get_state(context); } void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t num_bytes, void *data) @@ -68,29 +68,40 @@ void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data) } } -void PulseAudioDevice::playing(bool playing) +void PulseAudioDevice::runMixingThread() { - m_playback = playing; + for(;;) + { + { + std::lock_guard<ILockable> lock(*this); + + if(shouldStop()) + { + AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr); + doStop(); + return; + } + } + + if(AUD_pa_stream_is_corked(m_stream)) + AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr); - AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr); + AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); + } } PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) : - m_playback(false), m_state(PA_CONTEXT_UNCONNECTED), m_buffersize(buffersize), m_underflows(0) { - m_mainloop = AUD_pa_threaded_mainloop_new(); + m_mainloop = AUD_pa_mainloop_new(); - AUD_pa_threaded_mainloop_lock(m_mainloop); - - m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str()); + m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str()); if(!m_context) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); } @@ -99,26 +110,21 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff AUD_pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); - AUD_pa_threaded_mainloop_start(m_mainloop); - while(m_state != PA_CONTEXT_READY) { switch(m_state) { case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); break; default: - AUD_pa_threaded_mainloop_wait(m_mainloop); + AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); break; } } @@ -166,13 +172,10 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff if(!m_stream) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not create PulseAudio stream."); } @@ -188,32 +191,27 @@ PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buff buffer_attr.prebuf = -1U; buffer_attr.tlength = buffersize; - if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) + if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast<pa_stream_flags_t>(PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) { - AUD_pa_threaded_mainloop_unlock(m_mainloop); - AUD_pa_threaded_mainloop_stop(m_mainloop); - AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect PulseAudio stream."); } - AUD_pa_threaded_mainloop_unlock(m_mainloop); - create(); } PulseAudioDevice::~PulseAudioDevice() { - AUD_pa_threaded_mainloop_stop(m_mainloop); + stopMixingThread(); AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_threaded_mainloop_free(m_mainloop); + AUD_pa_mainloop_free(m_mainloop); destroy(); } diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index 9efae5128b1..be34cc9032b 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -26,7 +26,7 @@ * The PulseAudioDevice class. */ -#include "devices/SoftwareDevice.h" +#include "devices/ThreadedDevice.h" #include <pulse/pulseaudio.h> @@ -35,15 +35,10 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through PulseAudio, the simple direct media layer. */ -class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice +class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice { private: - /** - * Whether there is currently playback. - */ - volatile bool m_playback; - - pa_threaded_mainloop* m_mainloop; + pa_mainloop* m_mainloop; pa_context* m_context; pa_stream* m_stream; pa_context_state_t m_state; @@ -74,13 +69,15 @@ private: */ AUD_LOCAL static void PulseAudio_underflow(pa_stream* stream, void* data); + /** + * Streaming thread main function. + */ + AUD_LOCAL void runMixingThread(); + // delete copy constructor and operator= PulseAudioDevice(const PulseAudioDevice&) = delete; PulseAudioDevice& operator=(const PulseAudioDevice&) = delete; -protected: - virtual void playing(bool playing); - public: /** * Opens the PulseAudio audio device for playback. diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h index 9cefbc0c7e2..4b9e1ffea2b 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h @@ -24,18 +24,14 @@ PULSEAUDIO_SYMBOL(pa_context_unref); PULSEAUDIO_SYMBOL(pa_stream_begin_write); PULSEAUDIO_SYMBOL(pa_stream_connect_playback); PULSEAUDIO_SYMBOL(pa_stream_cork); +PULSEAUDIO_SYMBOL(pa_stream_is_corked); PULSEAUDIO_SYMBOL(pa_stream_new); PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr); PULSEAUDIO_SYMBOL(pa_stream_set_underflow_callback); PULSEAUDIO_SYMBOL(pa_stream_set_write_callback); PULSEAUDIO_SYMBOL(pa_stream_write); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_free); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_get_api); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_lock); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_new); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_signal); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_start); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_stop); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_unlock); -PULSEAUDIO_SYMBOL(pa_threaded_mainloop_wait); +PULSEAUDIO_SYMBOL(pa_mainloop_free); +PULSEAUDIO_SYMBOL(pa_mainloop_get_api); +PULSEAUDIO_SYMBOL(pa_mainloop_new); +PULSEAUDIO_SYMBOL(pa_mainloop_iterate); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp index 4f213dc8468..b4632ebb83e 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp @@ -31,159 +31,83 @@ template <class T> void SafeRelease(T **ppT) } } -void WASAPIDevice::start() -{ - lock(); - - // thread is still running, we can abort stopping it - if(m_stop) - m_stop = false; - // thread is not running, let's start it - else if(!m_playing) - { - if(m_thread.joinable()) - m_thread.join(); - - m_playing = true; - - m_thread = std::thread(&WASAPIDevice::updateStream, this); - } - - unlock(); -} - -void WASAPIDevice::updateStream() +void WASAPIDevice::runMixingThread() { UINT32 buffer_size; + UINT32 padding; + UINT32 length; data_t* buffer; - lock(); + IAudioRenderClient* render_client = nullptr; - if(FAILED(m_audio_client->GetBufferSize(&buffer_size))) { - m_playing = false; - m_stop = false; - unlock(); - return; - } + std::lock_guard<ILockable> lock(*this); - IAudioRenderClient* render_client = nullptr; - const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); + const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); - if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client)))) - { - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(m_audio_client->GetBufferSize(&buffer_size))) + goto init_error; - UINT32 padding; + if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast<void**>(&render_client)))) + goto init_error; - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(m_audio_client->GetCurrentPadding(&padding))) + goto init_error; - UINT32 length = buffer_size - padding; + length = buffer_size - padding; - if(FAILED(render_client->GetBuffer(length, &buffer))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(render_client->GetBuffer(length, &buffer))) + goto init_error; - mix((data_t*)buffer, length); + mix((data_t*)buffer, length); - if(FAILED(render_client->ReleaseBuffer(length, 0))) - { - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; + if(FAILED(render_client->ReleaseBuffer(length, 0))) + { + init_error: + SafeRelease(&render_client); + doStop(); + return; + } } - unlock(); - m_audio_client->Start(); auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); for(;;) { - lock(); - - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + std::lock_guard<ILockable> lock(*this); - length = buffer_size - padding; + if(FAILED(m_audio_client->GetCurrentPadding(&padding))) + goto stop_thread; - if(FAILED(render_client->GetBuffer(length, &buffer))) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + length = buffer_size - padding; - mix((data_t*)buffer, length); + if(FAILED(render_client->GetBuffer(length, &buffer))) + goto stop_thread; - if(FAILED(render_client->ReleaseBuffer(length, 0))) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + mix((data_t*)buffer, length); - // stop thread - if(m_stop) - { - m_audio_client->Stop(); - SafeRelease(&render_client); - m_playing = false; - m_stop = false; - unlock(); - return; - } + if(FAILED(render_client->ReleaseBuffer(length, 0))) + goto stop_thread; - unlock(); + // stop thread + if(shouldStop()) + { + stop_thread: + m_audio_client->Stop(); + SafeRelease(&render_client); + doStop(); + return; + } + } std::this_thread::sleep_for(sleepDuration); } } -void WASAPIDevice::playing(bool playing) -{ - if((!m_playing || m_stop) && playing) - start(); - else - m_stop = true; -} - WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : - m_playing(false), - m_stop(false), - m_imm_device_enumerator(nullptr), m_imm_device(nullptr), m_audio_client(nullptr), @@ -361,14 +285,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : WASAPIDevice::~WASAPIDevice() { - lock(); - - stopAll(); - - unlock(); - - if(m_thread.joinable()) - m_thread.join(); + stopMixingThread(); SafeRelease(&m_audio_client); SafeRelease(&m_imm_device); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h index ae25a09c432..375f03bd255 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.h +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h @@ -26,7 +26,7 @@ * The WASAPIDevice class. */ -#include "devices/SoftwareDevice.h" +#include "devices/ThreadedDevice.h" #include <thread> @@ -40,46 +40,23 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through WASAPI, the Windows audio API. */ -class AUD_PLUGIN_API WASAPIDevice : public SoftwareDevice +class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice { private: - /** - * Whether there is currently playback. - */ - bool m_playing; - - /** - * Whether the current playback should stop. - */ - bool m_stop; - IMMDeviceEnumerator* m_imm_device_enumerator; IMMDevice* m_imm_device; IAudioClient* m_audio_client; WAVEFORMATEXTENSIBLE m_wave_format_extensible; /** - * The streaming thread. - */ - std::thread m_thread; - - /** - * Starts the streaming thread. - */ - AUD_LOCAL void start(); - - /** * Streaming thread main function. */ - AUD_LOCAL void updateStream(); + AUD_LOCAL void runMixingThread(); // delete copy constructor and operator= WASAPIDevice(const WASAPIDevice&) = delete; WASAPIDevice& operator=(const WASAPIDevice&) = delete; -protected: - virtual void playing(bool playing); - public: /** * Opens the WASAPI audio device for playback. diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp index c8c1c6081c2..7a2561515f4 100644 --- a/extern/audaspace/src/devices/SoftwareDevice.cpp +++ b/extern/audaspace/src/devices/SoftwareDevice.cpp @@ -737,7 +737,7 @@ void SoftwareDevice::mix(data_t* buffer, int length) { m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs)); - std::lock_guard<std::recursive_mutex> lock(m_mutex); + std::lock_guard<ILockable> lock(*this); { std::shared_ptr<SoftwareDevice::SoftwareHandle> sound; @@ -880,7 +880,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<IReader> reader, b // play sound std::shared_ptr<SoftwareDevice::SoftwareHandle> sound = std::shared_ptr<SoftwareDevice::SoftwareHandle>(new SoftwareDevice::SoftwareHandle(this, reader, pitch, resampler, mapper, keep)); - std::lock_guard<std::recursive_mutex> lock(m_mutex); + std::lock_guard<ILockable> lock(*this); m_playingSounds.push_back(sound); @@ -897,7 +897,7 @@ std::shared_ptr<IHandle> SoftwareDevice::play(std::shared_ptr<ISound> sound, boo void SoftwareDevice::stopAll() { - std::lock_guard<std::recursive_mutex> lock(m_mutex); + std::lock_guard<ILockable> lock(*this); while(!m_playingSounds.empty()) m_playingSounds.front()->stop(); diff --git a/extern/audaspace/src/devices/ThreadedDevice.cpp b/extern/audaspace/src/devices/ThreadedDevice.cpp new file mode 100644 index 00000000000..44ceccb8a60 --- /dev/null +++ b/extern/audaspace/src/devices/ThreadedDevice.cpp @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright 2009-2016 Jörg Müller + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +#include "devices/ThreadedDevice.h" + +#include <mutex> + +AUD_NAMESPACE_BEGIN + +void ThreadedDevice::start() +{ + std::lock_guard<ILockable> lock(*this); + + // thread is still running, we can abort stopping it + if(m_stop) + m_stop = false; + + // thread is not running, let's start it + else if(!m_playing) + { + if(m_thread.joinable()) + m_thread.join(); + + m_playing = true; + + m_thread = std::thread(&ThreadedDevice::runMixingThread, this); + } +} + +void ThreadedDevice::playing(bool playing) +{ + if((!m_playing || m_stop) && playing) + start(); + else + m_stop = true; +} + +ThreadedDevice::ThreadedDevice() : + m_playing(false), + m_stop(false) +{ +} + +void aud::ThreadedDevice::stopMixingThread() +{ + stopAll(); + + if(m_thread.joinable()) + m_thread.join(); +} + +AUD_NAMESPACE_END diff --git a/extern/mantaflow/preprocessed/fileio/iovdb.cpp b/extern/mantaflow/preprocessed/fileio/iovdb.cpp index e615741e0f7..1846ef7ecbb 100644 --- a/extern/mantaflow/preprocessed/fileio/iovdb.cpp +++ b/extern/mantaflow/preprocessed/fileio/iovdb.cpp @@ -29,10 +29,10 @@ #if OPENVDB == 1 # include "openvdb/openvdb.h" -# include <openvdb/points/PointConversion.h> -# include <openvdb/points/PointCount.h> -# include <openvdb/tools/Clip.h> -# include <openvdb/tools/Dense.h> +# include "openvdb/points/PointConversion.h" +# include "openvdb/points/PointCount.h" +# include "openvdb/tools/Clip.h" +# include "openvdb/tools/Dense.h" #endif #define POSITION_NAME "P" @@ -519,7 +519,7 @@ int writeObjectsVDB(const string &filename, } } - // Write only if the is at least one grid, optionally write with compression. + // Write only if there is at least one grid, optionally write with compression. if (gridsVDB.size()) { int vdb_flags = openvdb::io::COMPRESS_ACTIVE_MASK; switch (compression) { @@ -534,7 +534,8 @@ int writeObjectsVDB(const string &filename, } case COMPRESSION_BLOSC: { # if OPENVDB_BLOSC == 1 - vdb_flags |= openvdb::io::COMPRESS_BLOSC; + // Cannot use |= here, causes segfault with blosc 1.5.0 (== recommended version) + vdb_flags = openvdb::io::COMPRESS_BLOSC; # else debMsg("OpenVDB was built without Blosc support, using Zip compression instead", 1); vdb_flags |= openvdb::io::COMPRESS_ZIP; diff --git a/extern/mantaflow/preprocessed/fluidsolver.h b/extern/mantaflow/preprocessed/fluidsolver.h index 0c871bca3a1..6770f8b7b05 100644 --- a/extern/mantaflow/preprocessed/fluidsolver.h +++ b/extern/mantaflow/preprocessed/fluidsolver.h @@ -384,6 +384,7 @@ class FluidSolver : public PbClass { GridStorage<Real> mGrids4dReal; GridStorage<Vec3> mGrids4dVec; GridStorage<Vec4> mGrids4dVec4; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/general.h b/extern/mantaflow/preprocessed/general.h index 50eac71e87e..8bf1c2e25de 100644 --- a/extern/mantaflow/preprocessed/general.h +++ b/extern/mantaflow/preprocessed/general.h @@ -42,7 +42,7 @@ inline void updateQtGui(bool full, int frame, float time, const std::string &cur # ifdef _DEBUG # define DEBUG 1 # endif // _DEBUG -#endif // DEBUG +#endif // DEBUG // Standard exception class Error : public std::exception { diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 1bb96fe3baa..03fd0112095 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit 39b7a415721ecbf6643612a24e8eadd221aeb934" +#define MANTA_GIT_VERSION "commit 9c505cd22e289b98c9aa717efba8ef3201c7e458" diff --git a/extern/mantaflow/preprocessed/grid.h b/extern/mantaflow/preprocessed/grid.h index 9c3d954771e..2c4296e78dd 100644 --- a/extern/mantaflow/preprocessed/grid.h +++ b/extern/mantaflow/preprocessed/grid.h @@ -389,6 +389,7 @@ class GridBase : public PbClass { Real mDx; bool m3D; // precomputed Z shift: to ensure 2D compatibility, always use this instead of sx*sy ! IndexInt mStrideZ; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/grid4d.h b/extern/mantaflow/preprocessed/grid4d.h index 9b64116fc9d..1741db590b7 100644 --- a/extern/mantaflow/preprocessed/grid4d.h +++ b/extern/mantaflow/preprocessed/grid4d.h @@ -326,6 +326,7 @@ class Grid4dBase : public PbClass { // precomputed Z,T shift: to ensure 2D compatibility, always use this instead of sx*sy ! IndexInt mStrideZ; IndexInt mStrideT; + public: PbArgs _args; } @@ -950,6 +951,7 @@ template<class T> class Grid4d : public Grid4dBase { protected: T *mData; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/levelset.h b/extern/mantaflow/preprocessed/levelset.h index db542bb1fe6..ae162f73c3d 100644 --- a/extern/mantaflow/preprocessed/levelset.h +++ b/extern/mantaflow/preprocessed/levelset.h @@ -266,6 +266,7 @@ class LevelsetGrid : public Grid<Real> { } static Real invalidTimeValue(); + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/mesh.h b/extern/mantaflow/preprocessed/mesh.h index d3a69abc4ea..b5de66ce095 100644 --- a/extern/mantaflow/preprocessed/mesh.h +++ b/extern/mantaflow/preprocessed/mesh.h @@ -796,6 +796,7 @@ class Mesh : public PbClass { std::vector<MeshDataImpl<int> *> mMdataInt; //! indicate that mdata of this mesh is copied, and needs to be freed bool mFreeMdata; + public: PbArgs _args; } @@ -881,6 +882,7 @@ class MeshDataBase : public PbClass { protected: Mesh *mMesh; + public: PbArgs _args; } @@ -1645,6 +1647,7 @@ template<class T> class MeshDataImpl : public MeshDataBase { //! optionally , we might have an associated grid from which to grab new data Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3 bool mGridSourceMAC; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/movingobs.h b/extern/mantaflow/preprocessed/movingobs.h index 0661ddf5b37..83ef6ed0c9f 100644 --- a/extern/mantaflow/preprocessed/movingobs.h +++ b/extern/mantaflow/preprocessed/movingobs.h @@ -154,6 +154,7 @@ class MovingObstacle : public PbClass { int mEmptyType; int mID; static int sIDcnt; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/noisefield.h b/extern/mantaflow/preprocessed/noisefield.h index 73c9de779ef..6ed8ac0012d 100644 --- a/extern/mantaflow/preprocessed/noisefield.h +++ b/extern/mantaflow/preprocessed/noisefield.h @@ -236,6 +236,7 @@ class WaveletNoiseField : public PbClass { static int randomSeed; // global reference count for noise tile static std::atomic<int> mNoiseReferenceCount; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/particle.h b/extern/mantaflow/preprocessed/particle.h index 7fcc7e5ca32..7e0c64e6d03 100644 --- a/extern/mantaflow/preprocessed/particle.h +++ b/extern/mantaflow/preprocessed/particle.h @@ -205,6 +205,7 @@ class ParticleBase : public PbClass { //! custom seed for particle systems, used by plugins int mSeed; //! fix global random seed storage, used mainly by functions in this class static int globalSeed; + public: PbArgs _args; } @@ -628,6 +629,7 @@ template<class S> class ParticleSystem : public ParticleBase { std::vector<S> mData; //! reduce storage , called by doCompress virtual void compress(); + public: PbArgs _args; } @@ -918,6 +920,7 @@ class ParticleIndexSystem : public ParticleSystem<ParticleIndexData> { return -1; } }; + public: PbArgs _args; } @@ -982,6 +985,7 @@ template<class DATA, class CON> class ConnectedParticleSystem : public ParticleS protected: std::vector<CON> mSegments; virtual void compress(); + public: PbArgs _args; } @@ -1071,6 +1075,7 @@ class ParticleDataBase : public PbClass { protected: ParticleBase *mpParticleSys; + public: PbArgs _args; } @@ -1843,6 +1848,7 @@ template<class T> class ParticleDataImpl : public ParticleDataBase { //! optionally , we might have an associated grid from which to grab new data Grid<T> *mpGridSource; //! unfortunately , we need to distinguish mac vs regular vec3 bool mGridSourceMAC; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp index 043660db20b..cf315429d65 100644 --- a/extern/mantaflow/preprocessed/plugin/meshplugins.cpp +++ b/extern/mantaflow/preprocessed/plugin/meshplugins.cpp @@ -234,10 +234,10 @@ void subdivideMesh( normalize(ne2); // Real thisArea = sqrMag(cross(-e2,e0)); - // small angle approximation says sin(x) = arcsin(x) = x, - // arccos(x) = pi/2 - arcsin(x), - // cos(x) = dot(A,B), - // so angle is approximately 1 - dot(A,B). + // small angle approximation says sin(x) = arcsin(x) = x, + // arccos(x) = pi/2 - arcsin(x), + // cos(x) = dot(A,B), + // so angle is approximately 1 - dot(A,B). Real angle[3]; angle[0] = 1.0 - dot(ne0, -ne2); angle[1] = 1.0 - dot(ne1, -ne0); diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp index 7a1d8224d94..2f876376f53 100644 --- a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp +++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp @@ -2287,9 +2287,10 @@ struct knFlipComputePotentialTrappedAir : public KernelBase { const Vec3 &vj = scaleFromManta * v.getCentered(x, y, z); const Vec3 xij = xi - xj; const Vec3 vij = vi - vj; - Real h = !pot.is3D() ? 1.414 * radius : - 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius - // for h, due to squared resp. cubic neighbor area + Real h = !pot.is3D() ? + 1.414 * radius : + 1.732 * radius; // estimate sqrt(2)*radius resp. sqrt(3)*radius for h, due + // to squared resp. cubic neighbor area vdiff += norm(vij) * (1 - dot(getNormalized(vij), getNormalized(xij))) * (1 - norm(xij) / h); } diff --git a/extern/mantaflow/preprocessed/shapes.h b/extern/mantaflow/preprocessed/shapes.h index fa645389bfe..5a400eaed09 100644 --- a/extern/mantaflow/preprocessed/shapes.h +++ b/extern/mantaflow/preprocessed/shapes.h @@ -269,6 +269,7 @@ class Shape : public PbClass { protected: GridType mType; + public: PbArgs _args; } @@ -319,6 +320,7 @@ class NullShape : public Shape { { gridSetConst<Real>(phi, 1000.0f); } + public: PbArgs _args; } @@ -394,6 +396,7 @@ class Box : public Shape { protected: Vec3 mP0, mP1; + public: PbArgs _args; } @@ -455,6 +458,7 @@ class Sphere : public Shape { protected: Vec3 mCenter, mScale; Real mRadius; + public: PbArgs _args; } @@ -579,6 +583,7 @@ class Cylinder : public Shape { protected: Vec3 mCenter, mZDir; Real mRadius, mZ; + public: PbArgs _args; } @@ -655,6 +660,7 @@ class Slope : public Shape { Real mAnglexy, mAngleyz; Real mOrigin; Vec3 mGs; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/turbulencepart.h b/extern/mantaflow/preprocessed/turbulencepart.h index 81c94d77722..5177aeb2d96 100644 --- a/extern/mantaflow/preprocessed/turbulencepart.h +++ b/extern/mantaflow/preprocessed/turbulencepart.h @@ -199,6 +199,7 @@ class TurbulenceParticleSystem : public ParticleSystem<TurbulenceParticleData> { private: WaveletNoiseField &noise; + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/vortexpart.h b/extern/mantaflow/preprocessed/vortexpart.h index e48fbc7f507..8f80cf910eb 100644 --- a/extern/mantaflow/preprocessed/vortexpart.h +++ b/extern/mantaflow/preprocessed/vortexpart.h @@ -127,6 +127,7 @@ class VortexParticleSystem : public ParticleSystem<VortexParticleData> { } virtual ParticleBase *clone(); + public: PbArgs _args; } diff --git a/extern/mantaflow/preprocessed/vortexsheet.h b/extern/mantaflow/preprocessed/vortexsheet.h index 01c32e4e806..0fc0f3a1258 100644 --- a/extern/mantaflow/preprocessed/vortexsheet.h +++ b/extern/mantaflow/preprocessed/vortexsheet.h @@ -240,6 +240,7 @@ class VortexSheetMesh : public Mesh { VorticityChannel mVorticity; TexCoord3Channel mTex1, mTex2; TurbulenceChannel mTurb; + public: PbArgs _args; } diff --git a/extern/smaa_areatex/CMakeLists.txt b/extern/smaa_areatex/CMakeLists.txt new file mode 100644 index 00000000000..2386b0e7b79 --- /dev/null +++ b/extern/smaa_areatex/CMakeLists.txt @@ -0,0 +1,26 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2017, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): IRIE Shinsuke +# +# ***** END GPL LICENSE BLOCK ***** + +add_executable(smaa_areatex smaa_areatex.cpp) diff --git a/extern/smaa_areatex/README.blender b/extern/smaa_areatex/README.blender new file mode 100644 index 00000000000..9c409142ae8 --- /dev/null +++ b/extern/smaa_areatex/README.blender @@ -0,0 +1,5 @@ +Project: smaa-cpp +URL: https://github.com/iRi-E/smaa-cpp +License: MIT +Upstream version: 0.4.0 +Local modifications: diff --git a/extern/smaa_areatex/smaa_areatex.cpp b/extern/smaa_areatex/smaa_areatex.cpp new file mode 100644 index 00000000000..7a4ff3a9831 --- /dev/null +++ b/extern/smaa_areatex/smaa_areatex.cpp @@ -0,0 +1,1208 @@ +/** + * Copyright (C) 2016-2017 IRIE Shinsuke + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * smaa_areatex.cpp version 0.4.0 + * + * This is a part of smaa-cpp that is an implementation of + * Enhanced Subpixel Morphological Antialiasing (SMAA) written in C++. + * + * This program is C++ rewrite of AreaTex.py included in the original + * SMAA ditribution: + * + * https://github.com/iryoku/smaa/tree/master/Scripts + */ + +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#include <cmath> + +/*------------------------------------------------------------------------------*/ +/* Type Definitions */ + +class Int2; +class Dbl2; + +class Int2 { +public: + int x, y; + + Int2() { this->x = this->y = 0; } + Int2(int x) { this->x = this->y = x; } + Int2(int x, int y) { this->x = x; this->y = y; } + + operator Dbl2(); + + Int2 operator + (Int2 other) { return Int2(x + other.x, y + other.y); } + Int2 operator * (Int2 other) { return Int2(x * other.x, y * other.y); } +}; + +class Dbl2 { +public: + double x, y; + + Dbl2() { this->x = this->y = 0.0; } + Dbl2(double x) { this->x = this->y = x; } + Dbl2(double x, double y) { this->x = x; this->y = y; } + + Dbl2 apply(double (* func)(double)) { return Dbl2(func(x), func(y)); } + + operator Int2(); + + Dbl2 operator + (Dbl2 other) { return Dbl2(x + other.x, y + other.y); } + Dbl2 operator - (Dbl2 other) { return Dbl2(x - other.x, y - other.y); } + Dbl2 operator * (Dbl2 other) { return Dbl2(x * other.x, y * other.y); } + Dbl2 operator / (Dbl2 other) { return Dbl2(x / other.x, y / other.y); } + Dbl2 operator += (Dbl2 other) { return Dbl2(x += other.x, y += other.y); } + bool operator == (Dbl2 other) { return (x == other.x && y == other.y); } +}; + +Int2::operator Dbl2() { return Dbl2((double)x, (double)y); } +Dbl2::operator Int2() { return Int2((int)x, (int)y); } + +/*------------------------------------------------------------------------------*/ +/* Data to Calculate Areatex */ + +/* Texture sizes: */ +/* (it's quite possible that this is not easily configurable) */ +static const int SUBSAMPLES_ORTHO = 7; +static const int SUBSAMPLES_DIAG = 5; +static const int MAX_DIST_ORTHO_COMPAT = 16; +static const int MAX_DIST_ORTHO = 20; +static const int MAX_DIST_DIAG = 20; +static const int TEX_SIZE_ORTHO = 80; /* 16 * 5 slots = 80 */ +static const int TEX_SIZE_DIAG = 80; /* 20 * 4 slots = 80 */ + +/* Number of samples for calculating areas in the diagonal textures: */ +/* (diagonal areas are calculated using brute force sampling) */ +static const int SAMPLES_DIAG = 30; + +/* Maximum distance for smoothing u-shapes: */ +static const int SMOOTH_MAX_DISTANCE = 32; + +/*------------------------------------------------------------------------------*/ +/* Offset Tables */ + +/* Offsets for subsample rendering */ +static const double subsample_offsets_ortho[SUBSAMPLES_ORTHO] = { + 0.0, /* 0 */ + -0.25, /* 1 */ + 0.25, /* 2 */ + -0.125, /* 3 */ + 0.125, /* 4 */ + -0.375, /* 5 */ + 0.375 /* 6 */ +}; + +static const Dbl2 subsample_offsets_diag[SUBSAMPLES_DIAG] = { + { 0.00, 0.00}, /* 0 */ + { 0.25, -0.25}, /* 1 */ + {-0.25, 0.25}, /* 2 */ + { 0.125, -0.125}, /* 3 */ + {-0.125, 0.125} /* 4 */ +}; + +/* Mapping offsets for placing each pattern subtexture into its place */ +enum edgesorthoIndices +{ + EDGESORTHO_NONE_NONE = 0, + EDGESORTHO_NONE_NEGA = 1, + EDGESORTHO_NONE_POSI = 2, + EDGESORTHO_NONE_BOTH = 3, + EDGESORTHO_NEGA_NONE = 4, + EDGESORTHO_NEGA_NEGA = 5, + EDGESORTHO_NEGA_POSI = 6, + EDGESORTHO_NEGA_BOTH = 7, + EDGESORTHO_POSI_NONE = 8, + EDGESORTHO_POSI_NEGA = 9, + EDGESORTHO_POSI_POSI = 10, + EDGESORTHO_POSI_BOTH = 11, + EDGESORTHO_BOTH_NONE = 12, + EDGESORTHO_BOTH_NEGA = 13, + EDGESORTHO_BOTH_POSI = 14, + EDGESORTHO_BOTH_BOTH = 15, +}; + +static const Int2 edgesortho_compat[16] = { + {0, 0}, {0, 1}, {0, 3}, {0, 4}, {1, 0}, {1, 1}, {1, 3}, {1, 4}, + {3, 0}, {3, 1}, {3, 3}, {3, 4}, {4, 0}, {4, 1}, {4, 3}, {4, 4} +}; + +static const Int2 edgesortho[16] = { + {0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3}, + {2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3} +}; + +enum edgesdiagIndices +{ + EDGESDIAG_NONE_NONE = 0, + EDGESDIAG_NONE_VERT = 1, + EDGESDIAG_NONE_HORZ = 2, + EDGESDIAG_NONE_BOTH = 3, + EDGESDIAG_VERT_NONE = 4, + EDGESDIAG_VERT_VERT = 5, + EDGESDIAG_VERT_HORZ = 6, + EDGESDIAG_VERT_BOTH = 7, + EDGESDIAG_HORZ_NONE = 8, + EDGESDIAG_HORZ_VERT = 9, + EDGESDIAG_HORZ_HORZ = 10, + EDGESDIAG_HORZ_BOTH = 11, + EDGESDIAG_BOTH_NONE = 12, + EDGESDIAG_BOTH_VERT = 13, + EDGESDIAG_BOTH_HORZ = 14, + EDGESDIAG_BOTH_BOTH = 15, +}; + +static const Int2 edgesdiag[16] = { + {0, 0}, {0, 1}, {0, 2}, {0, 3}, {1, 0}, {1, 1}, {1, 2}, {1, 3}, + {2, 0}, {2, 1}, {2, 2}, {2, 3}, {3, 0}, {3, 1}, {3, 2}, {3, 3} +}; + +/*------------------------------------------------------------------------------*/ +/* Miscellaneous Utility Functions */ + +/* Linear interpolation: */ +static Dbl2 lerp(Dbl2 a, Dbl2 b, double p) +{ + return a + (b - a) * Dbl2(p); +} + +/* Saturates a value to [0..1] range: */ +static double saturate(double x) +{ + return 0.0 < x ? (x < 1.0 ? x : 1.0) : 0.0; +} + +/*------------------------------------------------------------------------------*/ +/* Horizontal/Vertical Areas */ + +class AreaOrtho { + double m_data[SUBSAMPLES_ORTHO][TEX_SIZE_ORTHO][TEX_SIZE_ORTHO][2]; + bool m_compat; + bool m_orig_u; +public: + AreaOrtho(bool compat, bool orig_u) : m_compat(compat), m_orig_u(orig_u) {} + + double *getData() { return (double *)&m_data; } + Dbl2 getPixel(int offset_index, Int2 coords) { + return Dbl2(m_data[offset_index][coords.y][coords.x][0], + m_data[offset_index][coords.y][coords.x][1]); + } + + void areaTex(int offset_index); +private: + void putPixel(int offset_index, Int2 coords, Dbl2 pixel) { + m_data[offset_index][coords.y][coords.x][0] = pixel.x; + m_data[offset_index][coords.y][coords.x][1] = pixel.y; + } + + Dbl2 smoothArea(double d, Dbl2 a1, Dbl2 a2); + Dbl2 makeQuad(int x, double d, double o); + Dbl2 area(Dbl2 p1, Dbl2 p2, int x); + Dbl2 calculate(int pattern, int left, int right, double offset); +}; + +/* Smoothing function for small u-patterns: */ +Dbl2 AreaOrtho::smoothArea(double d, Dbl2 a1, Dbl2 a2) +{ + Dbl2 b1 = (a1 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5); + Dbl2 b2 = (a2 * Dbl2(2.0)).apply(sqrt) * Dbl2(0.5); + double p = saturate(d / (double)SMOOTH_MAX_DISTANCE); + return lerp(b1, a1, p) + lerp(b2, a2, p); +} + +/* Smoothing u-patterns by quadratic function: */ +Dbl2 AreaOrtho::makeQuad(int x, double d, double o) +{ + double r = (double)x; + + /* fmin() below is a trick to smooth tiny u-patterns: */ + return Dbl2(r, (1.0 - fmin(4.0, d) * r * (d - r) / (d * d)) * o); +} + +/* Calculates the area under the line p1->p2, for the pixel x..x+1: */ +Dbl2 AreaOrtho::area(Dbl2 p1, Dbl2 p2, int x) +{ + Dbl2 d = p2 - p1; + double x1 = (double)x; + double x2 = x1 + 1.0; + + if ((x1 >= p1.x && x1 < p2.x) || (x2 > p1.x && x2 <= p2.x)) { /* inside? */ + double y1 = p1.y + (x1 - p1.x) * d.y / d.x; + double y2 = p1.y + (x2 - p1.x) * d.y / d.x; + + if ((copysign(1.0, y1) == copysign(1.0, y2) || + fabs(y1) < 1e-4 || fabs(y2) < 1e-4)) { /* trapezoid? */ + double a = (y1 + y2) / 2.0; + if (a < 0.0) + return Dbl2(fabs(a), 0.0); + else + return Dbl2(0.0, fabs(a)); + } + else { /* Then, we got two triangles: */ + double x = p1.x - p1.y * d.x / d.y, xi; + double a1 = x > p1.x ? y1 * modf(x, &xi) / 2.0 : 0.0; + double a2 = x < p2.x ? y2 * (1.0 - modf(x, &xi)) / 2.0 : 0.0; + double a = fabs(a1) > fabs(a2) ? a1 : -a2; + if (a < 0.0) + return Dbl2(fabs(a1), fabs(a2)); + else + return Dbl2(fabs(a2), fabs(a1)); + } + } + else + return Dbl2(0.0, 0.0); +} + +/* Calculates the area for a given pattern and distances to the left and to the */ +/* right, biased by an offset: */ +Dbl2 AreaOrtho::calculate(int pattern, int left, int right, double offset) +{ + Dbl2 a1, a2; + + /* + * o1 | + * .-------´ + * o2 | + * + * <---d---> + */ + double d = (double)(left + right + 1); + + double o1 = 0.5 + offset; + double o2 = 0.5 + offset - 1.0; + + switch (pattern) { + case EDGESORTHO_NONE_NONE: + { + /* + * + * ------ + * + */ + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_POSI_NONE: + { + /* + * + * .------ + * | + * + * We only offset L patterns in the crossing edge side, to make it + * converge with the unfiltered pattern 0 (we don't want to filter the + * pattern 0 to avoid artifacts). + */ + if (left <= right) + return area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left); + else + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_NONE_POSI: + { + /* + * + * ------. + * | + */ + if (left >= right) + return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left); + else + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_POSI_POSI: + { + /* + * + * .------. + * | | + */ + if (m_orig_u) { + a1 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left); + a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left); + return smoothArea(d, a1, a2); + } + else + return area(makeQuad(left, d, o2), makeQuad(left + 1, d, o2), left); + break; + } + case EDGESORTHO_NEGA_NONE: + { + /* + * | + * `------ + * + */ + if (left <= right) + return area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left); + else + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_BOTH_NONE: + { + /* + * | + * +------ + * | + */ + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_NEGA_POSI: + { + /* + * | + * `------. + * | + * + * A problem of not offseting L patterns (see above), is that for certain + * max search distances, the pixels in the center of a Z pattern will + * detect the full Z pattern, while the pixels in the sides will detect a + * L pattern. To avoid discontinuities, we blend the full offsetted Z + * revectorization with partially offsetted L patterns. + */ + if (fabs(offset) > 0.0) { + a1 = area(Dbl2(0.0, o1), Dbl2(d, o2), left); + a2 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left); + a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o2), left); + return (a1 + a2) / Dbl2(2.0); + } + else + return area(Dbl2(0.0, o1), Dbl2(d, o2), left); + break; + } + case EDGESORTHO_BOTH_POSI: + { + /* + * | + * +------. + * | | + */ + return area(Dbl2(0.0, o1), Dbl2(d, o2), left); + break; + } + case EDGESORTHO_NONE_NEGA: + { + /* + * | + * ------´ + * + */ + if (left >= right) + return area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left); + else + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_POSI_NEGA: + { + /* + * | + * .------´ + * | + */ + if (fabs(offset) > 0.0) { + a1 = area(Dbl2(0.0, o2), Dbl2(d, o1), left); + a2 = area(Dbl2(0.0, o2), Dbl2(d / 2.0, 0.0), left); + a2 += area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left); + return (a1 + a2) / Dbl2(2.0); + } + else + return area(Dbl2(0.0, o2), Dbl2(d, o1), left); + break; + } + case EDGESORTHO_NONE_BOTH: + { + /* + * | + * ------+ + * | + */ + return Dbl2(0.0, 0.0); + break; + } + case EDGESORTHO_POSI_BOTH: + { + /* + * | + * .------+ + * | | + */ + return area(Dbl2(0.0, o2), Dbl2(d, o1), left); + break; + } + case EDGESORTHO_NEGA_NEGA: + { + /* + * | | + * `------´ + * + */ + if (m_orig_u) { + a1 = area(Dbl2(0.0, o1), Dbl2(d / 2.0, 0.0), left); + a2 = area(Dbl2(d / 2.0, 0.0), Dbl2(d, o1), left); + return smoothArea(d, a1, a2); + } + else + return area(makeQuad(left, d, o1), makeQuad(left + 1, d, o1), left); + break; + } + case EDGESORTHO_BOTH_NEGA: + { + /* + * | | + * +------´ + * | + */ + return area(Dbl2(0.0, o2), Dbl2(d, o1), left); + break; + } + case EDGESORTHO_NEGA_BOTH: + { + /* + * | | + * `------+ + * | + */ + return area(Dbl2(0.0, o1), Dbl2(d, o2), left); + break; + } + case EDGESORTHO_BOTH_BOTH: + { + /* + * | | + * +------+ + * | | + */ + return Dbl2(0.0, 0.0); + break; + } + } + + return Dbl2(0.0, 0.0); +} + +/*------------------------------------------------------------------------------*/ +/* Diagonal Areas */ + +class AreaDiag { + double m_data[SUBSAMPLES_DIAG][TEX_SIZE_DIAG][TEX_SIZE_DIAG][2]; + bool m_numeric; + bool m_orig_u; +public: + AreaDiag(bool numeric, bool orig_u) : m_numeric(numeric), m_orig_u(orig_u) {} + + double *getData() { return (double *)&m_data; } + Dbl2 getPixel(int offset_index, Int2 coords) { + return Dbl2(m_data[offset_index][coords.y][coords.x][0], + m_data[offset_index][coords.y][coords.x][1]); + } + + void areaTex(int offset_index); +private: + void putPixel(int offset_index, Int2 coords, Dbl2 pixel) { + m_data[offset_index][coords.y][coords.x][0] = pixel.x; + m_data[offset_index][coords.y][coords.x][1] = pixel.y; + } + + double area1(Dbl2 p1, Dbl2 p2, Int2 p); + Dbl2 area(Dbl2 p1, Dbl2 p2, int left); + Dbl2 areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left); + Dbl2 calculate(int pattern, int left, int right, Dbl2 offset); +}; + +/* Calculates the area under the line p1->p2 for the pixel 'p' using brute */ +/* force sampling: */ +/* (quick and dirty solution, but it works) */ +double AreaDiag::area1(Dbl2 p1, Dbl2 p2, Int2 p) +{ + if (p1 == p2) + return 1.0; + + double xm = (p1.x + p2.x) / 2.0, ym = (p1.y + p2.y) / 2.0; + double a = p2.y - p1.y; + double b = p1.x - p2.x; + int count = 0; + + for (int ix = 0; ix < SAMPLES_DIAG; ix++) { + double x = (double)p.x + (double)ix / (double)(SAMPLES_DIAG - 1); + for (int iy = 0; iy < SAMPLES_DIAG; iy++) { + double y = (double)p.y + (double)iy / (double)(SAMPLES_DIAG - 1); + if (a * (x - xm) + b * (y - ym) > 0.0) /* inside? */ + count++; + } + } + return (double)count / (double)(SAMPLES_DIAG * SAMPLES_DIAG); +} + +/* Calculates the area under the line p1->p2: */ +/* (includes the pixel and its opposite) */ +Dbl2 AreaDiag::area(Dbl2 p1, Dbl2 p2, int left) +{ + if (m_numeric) { + double a1 = area1(p1, p2, Int2(1, 0) + Int2(left)); + double a2 = area1(p1, p2, Int2(1, 1) + Int2(left)); + return Dbl2(1.0 - a1, a2); + } + + /* Calculates the area under the line p1->p2 for the pixel 'p' analytically */ + Dbl2 d = p2 - p1; + if (d.x == 0.0) + return Dbl2(0.0, 1.0); + + double x1 = (double)(1 + left); + double x2 = x1 + 1.0; + double ymid = x1; + double xtop = p1.x + (ymid + 1.0 - p1.y) * d.x / d.y; + double xmid = p1.x + (ymid - p1.y) * d.x / d.y; + double xbot = p1.x + (ymid - 1.0 - p1.y) * d.x / d.y; + + double y1 = p1.y + (x1 - p1.x) * d.y / d.x; + double y2 = p1.y + (x2 - p1.x) * d.y / d.x; + double fy1 = y1 - floor(y1); + double fy2 = y2 - floor(y2); + int iy1 = (int)floor(y1 - ymid); + int iy2 = (int)floor(y2 - ymid); + + if (iy1 <= -2) { + if (iy2 == -1) + return Dbl2(1.0 - (x2 - xbot) * fy2 * 0.5, 0.0); + else if (iy2 == 0) + return Dbl2((xmid + xbot) * 0.5 - x1, (x2 - xmid) * fy2 * 0.5); + else if (iy2 >= 1) + return Dbl2((xmid + xbot) * 0.5 - x1, x2 - (xtop + xmid) * 0.5); + else /* iy2 < -1 */ + return Dbl2(1.0, 0.0); + } + else if (iy1 == -1) { + if (iy2 == -1) + return Dbl2(1.0 - (fy1 + fy2) * 0.5, 0.0); + else if (iy2 == 0) + return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, (x2 - xmid) * fy2 * 0.5); + else if (iy2 >= 1) + return Dbl2((xmid - x1) * (1.0 - fy1) * 0.5, x2 - (xtop + xmid) * 0.5); + else /* iy2 < -1 */ + return Dbl2(1.0 - (xbot - x1) * fy1 * 0.5, 0.0); + } + else if (iy1 == 0) { + if (iy2 == -1) + return Dbl2((x2 - xmid) * (1.0 - fy2) * 0.5, (xmid - x1) * fy1 * 0.5); + else if (iy2 == 0) + return Dbl2(0.0, (fy1 + fy2) * 0.5); + else if (iy2 >= 1) + return Dbl2(0.0, 1.0 - (xtop - x1) * (1.0 - fy1) * 0.5); + else /* iy2 < -1 */ + return Dbl2(x2 - (xmid + xbot) * 0.5, (xmid - x1) * fy1 * 0.5); + } + else { /* iy1 > 0 */ + if (iy2 == -1) + return Dbl2((x2 - xtop) * (1.0 - fy2) * 0.5, (xtop + xmid) * 0.5 - x1); + else if (iy2 == 0) + return Dbl2(0.0, 1.0 - (x1 - xtop) * (1.0 - fy2) * 0.5); + else if (iy2 >= 1) + return Dbl2(0.0, 1.0); + else /* iy2 < -1 */ + return Dbl2(x2 - (xmid + xbot) * 0.5, (xtop + xmid) * 0.5 - x1); + } +} + +/* Calculate u-patterns using a triangle: */ +Dbl2 AreaDiag::areaTriangle(Dbl2 p1L, Dbl2 p2L, Dbl2 p1R, Dbl2 p2R, int left) +{ + double x1 = (double)(1 + left); + double x2 = x1 + 1.0; + + Dbl2 dL = p2L - p1L; + Dbl2 dR = p2R - p1R; + double xm = ((p1L.x * dL.y / dL.x - p1L.y) - (p1R.x * dR.y / dR.x - p1R.y)) / (dL.y / dL.x - dR.y / dR.x); + + double y1 = (x1 < xm) ? p1L.y + (x1 - p1L.x) * dL.y / dL.x : p1R.y + (x1 - p1R.x) * dR.y / dR.x; + double y2 = (x2 < xm) ? p1L.y + (x2 - p1L.x) * dL.y / dL.x : p1R.y + (x2 - p1R.x) * dR.y / dR.x; + + return area(Dbl2(x1, y1), Dbl2(x2, y2), left); +} + +/* Calculates the area for a given pattern and distances to the left and to the */ +/* right, biased by an offset: */ +Dbl2 AreaDiag::calculate(int pattern, int left, int right, Dbl2 offset) +{ + Dbl2 a1, a2; + + double d = (double)(left + right + 1); + + /* + * There is some Black Magic around diagonal area calculations. Unlike + * orthogonal patterns, the 'null' pattern (one without crossing edges) must be + * filtered, and the ends of both the 'null' and L patterns are not known: L + * and U patterns have different endings, and we don't know what is the + * adjacent pattern. So, what we do is calculate a blend of both possibilites. + */ + switch (pattern) { + case EDGESDIAG_NONE_NONE: + { + /* + * + * .-´ + * .-´ + * .-´ + * .-´ + * ´ + * + */ + a1 = area(Dbl2(1.0, 1.0), Dbl2(1.0, 1.0) + Dbl2(d), left); /* 1st possibility */ + a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d), left); /* 2nd possibility */ + return (a1 + a2) / Dbl2(2.0); /* Blend them */ + break; + } + case EDGESDIAG_VERT_NONE: + { + /* + * + * .-´ + * .-´ + * .-´ + * .-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_NONE_HORZ: + { + /* + * + * .---- + * .-´ + * .-´ + * .-´ + * ´ + * + */ + a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_VERT_HORZ: + { + /* + * + * .---- + * .-´ + * .-´ + * .-´ + * | + * | + */ + if (m_orig_u) + return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + else + return areaTriangle(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d), + Dbl2(0.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + break; + } + case EDGESDIAG_HORZ_NONE: + { + /* + * + * .-´ + * .-´ + * .-´ + * ----´ + * + * + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left); + a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_BOTH_NONE: + { + /* + * + * .-´ + * .-´ + * .-´ + * --.-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(0.0, 0.0) + Dbl2(d), left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d), left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_HORZ_HORZ: + { + /* + * + * .---- + * .-´ + * .-´ + * ----´ + * + * + */ + return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + break; + } + case EDGESDIAG_BOTH_HORZ: + { + /* + * + * .---- + * .-´ + * .-´ + * --.-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_NONE_VERT: + { + /* + * | + * | + * .-´ + * .-´ + * .-´ + * ´ + * + */ + a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_VERT_VERT: + { + /* + * | + * | + * .-´ + * .-´ + * .-´ + * | + * | + */ + return area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + break; + } + case EDGESDIAG_NONE_BOTH: + { + /* + * | + * .---- + * .-´ + * .-´ + * .-´ + * ´ + * + */ + a1 = area(Dbl2(0.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0), Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_VERT_BOTH: + { + /* + * | + * .---- + * .-´ + * .-´ + * .-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_HORZ_VERT: + { + /* + * | + * | + * .-´ + * .-´ + * ----´ + * + * + */ + if (m_orig_u) + return area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + else + return areaTriangle(Dbl2(1.0, 1.0) + offset, Dbl2(2.0, 1.0) + Dbl2(d), + Dbl2(1.0, 0.0), Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + break; + } + case EDGESDIAG_BOTH_VERT: + { + /* + * | + * | + * .-´ + * .-´ + * --.-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_HORZ_BOTH: + { + /* + * | + * .---- + * .-´ + * .-´ + * ----´ + * + * + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + case EDGESDIAG_BOTH_BOTH: + { + /* + * | + * .---- + * .-´ + * .-´ + * --.-´ + * | + * | + */ + a1 = area(Dbl2(1.0, 1.0) + offset, Dbl2(1.0, 1.0) + Dbl2(d) + offset, left); + a2 = area(Dbl2(1.0, 0.0) + offset, Dbl2(1.0, 0.0) + Dbl2(d) + offset, left); + return (a1 + a2) / Dbl2(2.0); + break; + } + } + + return Dbl2(0.0, 0.0); +} + +/*------------------------------------------------------------------------------*/ +/* Main Loops */ + +void AreaOrtho::areaTex(int offset_index) +{ + double offset = subsample_offsets_ortho[offset_index]; + int max_dist = m_compat ? MAX_DIST_ORTHO_COMPAT : MAX_DIST_ORTHO; + + for (int pattern = 0; pattern < 16; pattern++) { + Int2 e = Int2(max_dist) * (m_compat ? edgesortho_compat : edgesortho)[pattern]; + for (int left = 0; left < max_dist; left++) { + for (int right = 0; right < max_dist; right++) { + Dbl2 p = calculate(pattern, left * left, right * right, offset); + Int2 coords = e + Int2(left, right); + + putPixel(offset_index, coords, p); + } + } + } + return; +} + +void AreaDiag::areaTex(int offset_index) +{ + Dbl2 offset = subsample_offsets_diag[offset_index]; + + for (int pattern = 0; pattern < 16; pattern++) { + Int2 e = Int2(MAX_DIST_DIAG) * edgesdiag[pattern]; + for (int left = 0; left < MAX_DIST_DIAG; left++) { + for (int right = 0; right < MAX_DIST_DIAG; right++) { + Dbl2 p = calculate(pattern, left, right, offset); + Int2 coords = e + Int2(left, right); + + putPixel(offset_index, coords, p); + } + } + } + return; +} + +/*------------------------------------------------------------------------------*/ +/* Write File to Specified Location on Disk */ + +/* C/C++ source code (arrays of floats) */ +static void write_double_array(FILE *fp, const double *ptr, int length, const char *array_name, bool quantize) +{ + fprintf(fp, "static const float %s[%d] = {", array_name, length); + + for (int n = 0; n < length; n++) { + if (n > 0) + fprintf(fp, ","); + fprintf(fp, (n % 8 != 0) ? " " : "\n\t"); + + if (quantize) + fprintf(fp, "%3d / 255.0", (int)(*(ptr++) * 255.0)); + else + fprintf(fp, "%1.8lf", *(ptr++)); + } + + fprintf(fp, "\n};\n"); +} + +static void write_csource(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling, bool quantize) +{ + fprintf(fp, "/* This file was generated by smaa_areatex.cpp */\n"); + + fprintf(fp, "\n/* Horizontal/Vertical Areas */\n"); + write_double_array(fp, ortho->getData(), + TEX_SIZE_ORTHO * TEX_SIZE_ORTHO * 2 * (subsampling ? SUBSAMPLES_ORTHO : 1), + "areatex", quantize); + + fprintf(fp, "\n/* Diagonal Areas */\n"); + write_double_array(fp, diag->getData(), + TEX_SIZE_DIAG * TEX_SIZE_DIAG * 2 * (subsampling ? SUBSAMPLES_DIAG : 1), + "areatex_diag", quantize); +} + +/* .tga File (RGBA 32bit uncompressed) */ +static void write_tga(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling) +{ + int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1; + unsigned char header[18] = {0, 0, + 2, /* uncompressed RGB */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, /* 32bit */ + 8}; /* 8bit alpha, left to right, bottom to top */ + + /* Set width and height */ + header[12] = (TEX_SIZE_ORTHO + TEX_SIZE_DIAG) & 0xff; + header[13] = ((TEX_SIZE_ORTHO + TEX_SIZE_DIAG) >> 8) & 0xff; + header[14] = (subsamples * TEX_SIZE_ORTHO) & 0xff; + header[15] = ((subsamples * TEX_SIZE_ORTHO) >> 8) & 0xff; + + /* Write .tga header */ + fwrite(header, sizeof(unsigned char), sizeof(header) / sizeof(unsigned char), fp); + + /* Write pixel data */ + for (int i = subsamples - 1; i >= 0; i--) { + for (int y = TEX_SIZE_ORTHO - 1; y >= 0; y--) { + for (int x = 0; x < TEX_SIZE_ORTHO; x++) { + Dbl2 p = ortho->getPixel(i, Int2(x, y)); + fputc(0, fp); /* B */ + fputc((unsigned char)(p.y * 255.0), fp); /* G */ + fputc((unsigned char)(p.x * 255.0), fp); /* R */ + fputc(0, fp); /* A */ + } + + for (int x = 0; x < TEX_SIZE_DIAG; x++) { + if (i < SUBSAMPLES_DIAG) { + Dbl2 p = diag->getPixel(i, Int2(x, y)); + fputc(0, fp); /* B */ + fputc((unsigned char)(p.y * 255.0), fp); /* G */ + fputc((unsigned char)(p.x * 255.0), fp); /* R */ + fputc(0, fp); /* A */ + } + else { + fputc(0, fp); + fputc(0, fp); + fputc(0, fp); + fputc(0, fp); + } + } + } + } +} + +/* .raw File (R8G8 raw data) */ +static void write_raw(AreaOrtho *ortho, AreaDiag *diag, FILE *fp, bool subsampling) +{ + int subsamples = subsampling ? SUBSAMPLES_ORTHO : 1; + + /* Write pixel data */ + for (int i = 0; i < subsamples; i++) { + for (int y = 0; y < TEX_SIZE_ORTHO; y++) { + for (int x = 0; x < TEX_SIZE_ORTHO; x++) { + Dbl2 p = ortho->getPixel(i, Int2(x, y)); + fputc((unsigned char)(p.x * 255.0), fp); /* R */ + fputc((unsigned char)(p.y * 255.0), fp); /* G */ + } + + for (int x = 0; x < TEX_SIZE_DIAG; x++) { + if (i < SUBSAMPLES_DIAG) { + Dbl2 p = diag->getPixel(i, Int2(x, y)); + fputc((unsigned char)(p.x * 255.0), fp); /* R */ + fputc((unsigned char)(p.y * 255.0), fp); /* G */ + } + else { + fputc(0, fp); + fputc(0, fp); + } + } + } + } +} + +static int generate_file(AreaOrtho *ortho, AreaDiag *diag, const char *path, bool subsampling, bool quantize, bool tga, bool raw) +{ + FILE *fp = fopen(path, tga ? "wb" : "w"); + + if (!fp) { + fprintf(stderr, "Unable to open file: %s\n", path); + return 1; + } + + // fprintf(stderr, "Generating %s\n", path); + + if (tga) + write_tga(ortho, diag, fp, subsampling); + else if (raw) + write_raw(ortho, diag, fp, subsampling); + else + write_csource(ortho, diag, fp, subsampling, quantize); + + fclose(fp); + + return 0; +} + +int main(int argc, char **argv) +{ + bool subsampling = false; + bool quantize = false; + bool tga = false; + bool raw = false; + bool compat = false; + bool numeric = false; + bool orig_u = false; + bool help = false; + char *outfile = NULL; + int status = 0; + + for (int i = 1; i < argc; i++) { + char *ptr = argv[i]; + if (*ptr++ == '-' && *ptr != '\0') { + char c; + while ((c = *ptr++) != '\0') { + if (c == 's') + subsampling = true; + else if (c == 'q') + quantize = true; + else if (c == 't') + tga = true; + else if (c == 'r') + raw = true; + else if (c == 'c') + compat = true; + else if (c == 'n') + numeric = true; + else if (c == 'u') + orig_u = true; + else if (c == 'h') + help = true; + else { + fprintf(stderr, "Unknown option: -%c\n", c); + status = 1; + break; + } + } + } + else if (outfile) { + fprintf(stderr, "Too much file names: %s, %s\n", outfile, argv[i]); + status = 1; + } + else + outfile = argv[i]; + + if (status != 0) + break; + } + + if (status == 0 && !help && !outfile) { + fprintf(stderr, "File name was not specified.\n"); + status = 1; + } + + if (status != 0 || help) { + fprintf(stderr, "Usage: %s [OPTION]... OUTFILE\n", argv[0]); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -s Calculate data for subpixel rendering\n"); + fprintf(stderr, " -q Quantize data to 256 levels\n"); + fprintf(stderr, " -t Write TGA image instead of C/C++ source\n"); + fprintf(stderr, " -r Write R8G8 raw image instead of C/C++ source\n"); + fprintf(stderr, " -c Generate compatible orthogonal data that subtexture size is 16\n"); + fprintf(stderr, " -n Numerically calculate diagonal data using brute force sampling\n"); + fprintf(stderr, " -u Process orthogonal / diagonal U patterns in older ways\n"); + fprintf(stderr, " -h Print this help and exit\n"); + fprintf(stderr, "File name OUTFILE usually should have an extension such as .c, .h, or .tga,\n"); + fprintf(stderr, "except for a special name '-' that means standard output.\n\n"); + fprintf(stderr, "Example:\n"); + fprintf(stderr, " Generate TGA file exactly same as AreaTexDX10.tga bundled with the\n"); + fprintf(stderr, " original implementation:\n\n"); + fprintf(stderr, " $ smaa_areatex -stcnu AreaTexDX10.tga\n\n"); + return status; + } + + AreaOrtho *ortho = new AreaOrtho(compat, orig_u); + AreaDiag *diag = new AreaDiag(numeric, orig_u); + + /* Calculate areatex data */ + for (int i = 0; i < (subsampling ? SUBSAMPLES_ORTHO : 1); i++) + ortho->areaTex(i); + + for (int i = 0; i < (subsampling ? SUBSAMPLES_DIAG : 1); i++) + diag->areaTex(i); + + /* Generate .tga, .raw, or C/C++ source file, or write the data to stdout */ + if (strcmp(outfile, "-") != 0) + status = generate_file(ortho, diag, outfile, subsampling, quantize, tga, raw); + else if (tga) + write_tga(ortho, diag, stdout, subsampling); + else if (raw) + write_raw(ortho, diag, stdout, subsampling); + else + write_csource(ortho, diag, stdout, subsampling, quantize); + + delete ortho; + delete diag; + + return status; +} + +/* smaa_areatex.cpp ends here */ |