├── .gitignore ├── .gitmodules ├── .travis.yml ├── README.md ├── docs └── source │ ├── change_log.rst │ ├── conf.py │ ├── cpp_api_example.rst │ ├── index.rst │ ├── presets.rst │ └── usage.rst ├── include ├── f3kdb.h ├── f3kdb_enums.h └── f3kdb_params.h ├── license.txt ├── msvc ├── f3kdb_test.vcxproj ├── f3kdb_test.vcxproj.filters ├── flash3kyuu_deband.vcxproj ├── flash3kyuu_deband.vcxproj.filters └── wscript ├── src ├── auto_utils.cpp ├── auto_utils.h ├── auto_utils_helper.h ├── avisynth │ ├── avisynth.h │ ├── avisynth_plugin.hpp │ ├── avisynth_plugin_v6.cpp │ ├── avs │ │ ├── alignment.h │ │ ├── capi.h │ │ ├── config.h │ │ ├── cpuid.h │ │ ├── minmax.h │ │ ├── types.h │ │ └── win.h │ ├── check.hpp │ ├── filter.h │ ├── filter_impl.hpp │ ├── flash3kyuu_deband.def.h │ ├── mt_info.hpp │ └── stdafx.h ├── compiler_compat.h ├── constants.h ├── core.cpp ├── core.h ├── debug_dump.cpp ├── debug_dump.h ├── dither_high.h ├── dllmain.cpp ├── flash3kyuu_deband_impl_c.cpp ├── flash3kyuu_deband_impl_sse2.cpp ├── flash3kyuu_deband_impl_sse4.cpp ├── flash3kyuu_deband_impl_ssse3.cpp ├── flash3kyuu_deband_sse_base.h ├── gen_filter_def.py ├── icc_override.cpp ├── icc_override.h ├── impl_dispatch.cpp ├── impl_dispatch.h ├── impl_dispatch_decl.h ├── pixel_proc_c.h ├── pixel_proc_c_16bit.h ├── pixel_proc_c_high_bit_depth_common.h ├── pixel_proc_c_high_f_s_dithering.h ├── pixel_proc_c_high_no_dithering.h ├── pixel_proc_c_high_ordered_dithering.h ├── presets.h ├── process_plane_context.cpp ├── process_plane_context.h ├── public_interface.cpp ├── random.cpp ├── random.h ├── sse_compat.h ├── sse_utils.h ├── stdafx.cpp ├── stdafx.h ├── targetver.h ├── utils.h ├── vapoursynth │ ├── VSHelper.h │ ├── VapourSynth.h │ ├── plugin.cpp │ ├── plugin.def.h │ └── plugin.h ├── wscript └── x64_compat.h ├── test ├── build_core_param_set.py ├── case_frames │ ├── wallorig_320x180_1_1_1_10.yuv │ ├── wallorig_320x180_1_1_2_10.yuv │ ├── wallorig_320x180_1_1_2_16.yuv │ ├── wallorig_640x360_1_1_0_8.yuv │ └── wallorignoise_320x180_1_1_1_16.yuv ├── gtest_stub.h ├── stdafx.cpp ├── stdafx.h ├── targetver.h ├── test_core.cpp ├── test_core_param_set.h ├── test_params_from_string.cpp └── wscript ├── waf └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.user 3 | Debug/* 4 | Debug with dump/* 5 | Release/* 6 | Release_MSVC/* 7 | x64/* 8 | 9 | test/Debug 10 | test/Release 11 | test/x64 12 | 13 | *.dyn 14 | *.obj 15 | *.sublime-* 16 | .lock-waf* 17 | .waf-* 18 | build 19 | .waf3-* 20 | sandbox 21 | msvc/Debug 22 | msvc/Release* 23 | msvc/x64/ 24 | waf-* 25 | waf3-* 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/googletest"] 2 | path = lib/googletest 3 | url = https://github.com/SAPikachu/googletest.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | before_install: 11 | - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y 12 | - sudo apt-get update -qq 13 | - sudo apt-get install -qq python3 14 | - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi 15 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 16 | 17 | script: > 18 | ./waf configure --enable-test && 19 | ./waf build && 20 | build/test/f3kdb-test --gtest_filter='-Core/*' && 21 | build/test/f3kdb-test --gtest_filter='Core/*/?:Core/*/??' && 22 | build/test/f3kdb-test --gtest_filter='Core/*/?4?:Core/*/?7?' && 23 | build/test/f3kdb-test --gtest_filter='Core/*/??3?:Core/*/??6?' 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | f3kdb (a.k.a. flash3kyuu_deband) 2 | ================================ 3 | 4 | [![Build Status](https://travis-ci.org/SAPikachu/flash3kyuu_deband.png)](https://travis-ci.org/SAPikachu/flash3kyuu_deband) 5 | 6 | [Documentation](https://f3kdb.readthedocs.org/) 7 | 8 | How to build (Linux) 9 | -------------------- 10 | 11 | ### Before compiling 12 | 13 | * Ensure Python 3 is installed 14 | * GCC 4.8.1+ or Clang 3.2+ is required, please install either one 15 | * Run `git submodule update --init --recursive` to initialize dependencies (Only needed for building tests) 16 | 17 | ### Configuration 18 | 19 | To configure for compiling using default compiler in your system, run: 20 | 21 | ./waf configure 22 | 23 | If you need to use a non-default compiler, use this: 24 | 25 | CC={your c compiler} CXX={your c++ compiler} ./waf configure 26 | 27 | To build test cases, append `--enable-test` after the command line. 28 | 29 | ### Compiling 30 | 31 | Simply run: 32 | 33 | ./waf build 34 | 35 | Optionally, you can install f3kdb into your system: 36 | 37 | sudo ./waf install 38 | 39 | ### Run test cases 40 | 41 | To run all test cases (may need 20 ~ 30 minutes): 42 | 43 | build/test/f3kdb-test 44 | 45 | If you want to run only a subset of tests, check `.travis.yml` for command line examples. 46 | -------------------------------------------------------------------------------- /docs/source/change_log.rst: -------------------------------------------------------------------------------- 1 | Change log 2 | ---------- 3 | 4 | 2.1 5 | *** 6 | 7 | * Support AviSynth+ native high bit depth 8 | 9 | 2.0.0 10 | ***** 11 | 12 | * Support Vapoursynth natively 13 | 14 | * New parameter: preset 15 | 16 | * C++ API support 17 | 18 | * Dropped YUY2 support, please process in YV16 instead 19 | 20 | * Dropped f3kdb_dither, please use ``f3kdb(preset="depth", ...)`` for bitdepth conversion 21 | 22 | * Dropped several deprecated parameter values 23 | 24 | 1.5.1 (2012-04-07) 25 | ****************** 26 | 27 | * Supports setting StdDev (sigma) for the Gaussian random number generator 28 | 29 | 1.5.0 (2012-03-12) 30 | ****************** 31 | 32 | * (There isn't any new feature in this version, only some parameters are 33 | modified to reduce user confusion) 34 | 35 | * ditherY/ditherC are renamed to grainY/grainC 36 | 37 | * dynamic_dither_noise is renamed to dynamic_grain 38 | 39 | * precision_mode is renamed to dither_algo, mode 4 and 5 are removed 40 | 41 | * random_algo_dither is renamed to random_algo_grain 42 | 43 | * enable_fast_skip_plane is removed, this optimization will be enabled 44 | implicitly whenever possible (Filter result won't be changed by this 45 | optimization) 46 | 47 | 1.4.2 (2011-11-10) 48 | ****************** 49 | 50 | * Fixed crash on some non-mod16 videos 51 | 52 | 1.4.1 (2011-11-05) 53 | ****************** 54 | 55 | * Fixed broken YUY2 support (still slow) 56 | 57 | * Improved default value handling of bitdepth-related parameters 58 | 59 | * precision_mode 4 / 5 are now deprecated and may be removed in future versions, 60 | you can use output_mode 1 / 2 to achieve the same result 61 | 62 | 1.4.0 (2011-10-30) 63 | ****************** 64 | 65 | * 9 ~ 16 bit-depth input/output 66 | 67 | * Related parameters: input_mode/input_depth/output_mode/output_depth 68 | 69 | * New random number generator, reference position and dither noise can be generated in uniform or gaussian distribution 70 | 71 | * Related parameters: random_algo_ref / random_algo_dither 72 | 73 | * diff_seed is replaced with dynamic_dither_noise, when enabled, noise pattern will be different for each frame 74 | 75 | * Another new parameter: enable_fast_skip_plane 76 | 77 | * Short filter alias: f3kdb 78 | 79 | * Now the ICC-compiled DLL should be runnable on pre-SSE2 systems (untested) 80 | 81 | * Several bug fixes 82 | 83 | 1.3.0 (2011-09-07) 84 | ****************** 85 | 86 | * Added x64 version 87 | 88 | * Added a downsample filter: f3kdb_dither 89 | 90 | * Internal precision is increased to 16bit 91 | 92 | * New parameter: keep_tv_range, please see readme.txt for details 93 | 94 | * Default sample_mode is changed to 2 as it is better in most cases 95 | 96 | * Fixed: Floyd-Steinberg dithering may produce incorrect result for full-range videos 97 | 98 | * Fixed: Broken YUY2 debanding 99 | 100 | * Minor optimizations 101 | 102 | 1.2.0 (2011-08-01) 103 | ****************** 104 | 105 | * Added support for YUY2 (not optimized yet) 106 | 107 | * Added support for all planar YUV format in AviSynth 2.6 108 | 109 | * The filter is now compatible with both AviSynth 2.5 and 2.6 110 | 111 | * 16bit output (precision_mode = 4/5) 112 | 113 | * Note: The internal processing precision is still 14bit, this will be improved in future versions 114 | 115 | 1.1.0 (2011-06-18) 116 | ****************** 117 | 118 | * Fixed a bug that high threshold values would produce incorrect result in 119 | high precision modes. 120 | 121 | * Threshold values was scaled based on other parameter in previous versions, 122 | it is unscaled now to increase flexibility. Using same value has weaker 123 | effect in new version. Effect of default parameter set is also changed. 124 | 125 | * SSE optimization for high precision mode. 126 | 127 | * Rejects some invalid parameter combination instead of silently skips them 128 | 129 | 1.0.2 (2011-06-06) 130 | ****************** 131 | 132 | * High precision mode 133 | 134 | * (currently non-optimized, SSE routine will be added later) 135 | 136 | * Frame edges are properly processed now 137 | 138 | * Fix crash in some cases (unaligned frames are handled correctly) 139 | 140 | * Other bug fixes 141 | 142 | 1.0.1 (2011-05-27) 143 | ****************** 144 | 145 | * Multi-threaded processing 146 | 147 | * Skip planes which threshold is 0 148 | 149 | 1.0.0 (2011-05-21) 150 | ****************** 151 | 152 | * Lots of bug fix 153 | 154 | * SSE optimization, massive speed up 155 | 156 | 0.9.1 (2011-05-05) 157 | ****************** 158 | 159 | * Fix: Incorrect results when blur_first=true 160 | 161 | 0.9 (2011-05-04) 162 | ****************** 163 | 164 | * Initial release 165 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # f3kdb documentation build configuration file, created by 5 | # sphinx-quickstart on Fri Oct 4 20:41:22 2013. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The encoding of source files. 41 | #source_encoding = 'utf-8-sig' 42 | 43 | # The master toctree document. 44 | master_doc = 'index' 45 | 46 | # General information about the project. 47 | project = 'f3kdb' 48 | copyright = '2011 ~ 2013, SAPikachu' 49 | 50 | # The version info for the project you're documenting, acts as replacement for 51 | # |version| and |release|, also used in various other places throughout the 52 | # built documents. 53 | # 54 | # The short X.Y version. 55 | version = '2.0' 56 | # The full version, including alpha/beta/rc tags. 57 | release = '2.0' 58 | 59 | # The language for content autogenerated by Sphinx. Refer to documentation 60 | # for a list of supported languages. 61 | #language = None 62 | 63 | # There are two options for replacing |today|: either, you set today to some 64 | # non-false value, then it is used: 65 | #today = '' 66 | # Else, today_fmt is used as the format for a strftime call. 67 | #today_fmt = '%B %d, %Y' 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | exclude_patterns = [] 72 | 73 | # The reST default role (used for this markup: `text`) to use for all 74 | # documents. 75 | #default_role = None 76 | 77 | # If true, '()' will be appended to :func: etc. cross-reference text. 78 | #add_function_parentheses = True 79 | 80 | # If true, the current module name will be prepended to all description 81 | # unit titles (such as .. function::). 82 | #add_module_names = True 83 | 84 | # If true, sectionauthor and moduleauthor directives will be shown in the 85 | # output. They are ignored by default. 86 | #show_authors = False 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | pygments_style = 'sphinx' 90 | 91 | # A list of ignored prefixes for module index sorting. 92 | #modindex_common_prefix = [] 93 | 94 | # If true, keep warnings as "system message" paragraphs in the built documents. 95 | #keep_warnings = False 96 | 97 | 98 | # -- Options for HTML output ---------------------------------------------- 99 | 100 | # The theme to use for HTML and HTML Help pages. See the documentation for 101 | # a list of builtin themes. 102 | html_theme = 'default' 103 | 104 | # Theme options are theme-specific and customize the look and feel of a theme 105 | # further. For a list of options available for each theme, see the 106 | # documentation. 107 | html_theme_options = { 108 | 'nosidebar': True 109 | } 110 | 111 | # Add any paths that contain custom themes here, relative to this directory. 112 | #html_theme_path = [] 113 | 114 | # The name for this set of Sphinx documents. If None, it defaults to 115 | # " v documentation". 116 | #html_title = None 117 | 118 | # A shorter title for the navigation bar. Default is the same as html_title. 119 | #html_short_title = None 120 | 121 | # The name of an image file (relative to this directory) to place at the top 122 | # of the sidebar. 123 | #html_logo = None 124 | 125 | # The name of an image file (within the static path) to use as favicon of the 126 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 127 | # pixels large. 128 | #html_favicon = None 129 | 130 | # Add any paths that contain custom static files (such as style sheets) here, 131 | # relative to this directory. They are copied after the builtin static files, 132 | # so a file named "default.css" will overwrite the builtin "default.css". 133 | html_static_path = ['_static'] 134 | 135 | # Add any extra paths that contain custom files (such as robots.txt or 136 | # .htaccess) here, relative to this directory. These files are copied 137 | # directly to the root of the documentation. 138 | #html_extra_path = [] 139 | 140 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 141 | # using the given strftime format. 142 | #html_last_updated_fmt = '%b %d, %Y' 143 | 144 | # If true, SmartyPants will be used to convert quotes and dashes to 145 | # typographically correct entities. 146 | #html_use_smartypants = True 147 | 148 | # Custom sidebar templates, maps document names to template names. 149 | #html_sidebars = {} 150 | 151 | # Additional templates that should be rendered to pages, maps page names to 152 | # template names. 153 | #html_additional_pages = {} 154 | 155 | # If false, no module index is generated. 156 | html_domain_indices = False 157 | 158 | # If false, no index is generated. 159 | html_use_index = False 160 | 161 | # If true, the index is split into individual pages for each letter. 162 | #html_split_index = False 163 | 164 | # If true, links to the reST sources are added to the pages. 165 | #html_show_sourcelink = True 166 | 167 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 168 | #html_show_sphinx = True 169 | 170 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 171 | #html_show_copyright = True 172 | 173 | # If true, an OpenSearch description file will be output, and all pages will 174 | # contain a tag referring to it. The value of this option must be the 175 | # base URL from which the finished HTML is served. 176 | #html_use_opensearch = '' 177 | 178 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 179 | #html_file_suffix = None 180 | 181 | # Output file base name for HTML help builder. 182 | htmlhelp_basename = 'f3kdbdoc' 183 | 184 | 185 | # -- Options for LaTeX output --------------------------------------------- 186 | 187 | latex_elements = { 188 | # The paper size ('letterpaper' or 'a4paper'). 189 | #'papersize': 'letterpaper', 190 | 191 | # The font size ('10pt', '11pt' or '12pt'). 192 | #'pointsize': '10pt', 193 | 194 | # Additional stuff for the LaTeX preamble. 195 | #'preamble': '', 196 | } 197 | 198 | # Grouping the document tree into LaTeX files. List of tuples 199 | # (source start file, target name, title, 200 | # author, documentclass [howto, manual, or own class]). 201 | latex_documents = [ 202 | ('index', 'f3kdb.tex', 'f3kdb Documentation', 203 | 'SAPikachu', 'manual'), 204 | ] 205 | 206 | # The name of an image file (relative to this directory) to place at the top of 207 | # the title page. 208 | #latex_logo = None 209 | 210 | # For "manual" documents, if this is true, then toplevel headings are parts, 211 | # not chapters. 212 | #latex_use_parts = False 213 | 214 | # If true, show page references after internal links. 215 | #latex_show_pagerefs = False 216 | 217 | # If true, show URL addresses after external links. 218 | #latex_show_urls = False 219 | 220 | # Documents to append as an appendix to all manuals. 221 | #latex_appendices = [] 222 | 223 | # If false, no module index is generated. 224 | #latex_domain_indices = True 225 | 226 | 227 | # -- Options for manual page output --------------------------------------- 228 | 229 | # One entry per manual page. List of tuples 230 | # (source start file, name, description, authors, manual section). 231 | man_pages = [ 232 | ('index', 'f3kdb', 'f3kdb Documentation', 233 | ['SAPikachu'], 1) 234 | ] 235 | 236 | # If true, show URL addresses after external links. 237 | #man_show_urls = False 238 | 239 | 240 | # -- Options for Texinfo output ------------------------------------------- 241 | 242 | # Grouping the document tree into Texinfo files. List of tuples 243 | # (source start file, target name, title, author, 244 | # dir menu entry, description, category) 245 | texinfo_documents = [ 246 | ('index', 'f3kdb', 'f3kdb Documentation', 247 | 'SAPikachu', 'f3kdb', 'One line description of project.', 248 | 'Miscellaneous'), 249 | ] 250 | 251 | # Documents to append as an appendix to all manuals. 252 | #texinfo_appendices = [] 253 | 254 | # If false, no module index is generated. 255 | #texinfo_domain_indices = True 256 | 257 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 258 | #texinfo_show_urls = 'footnote' 259 | 260 | # If true, do not generate a @detailmenu in the "Top" node's menu. 261 | #texinfo_no_detailmenu = False 262 | -------------------------------------------------------------------------------- /docs/source/cpp_api_example.rst: -------------------------------------------------------------------------------- 1 | C++ API 2 | ------- 3 | 4 | .. highlight:: c 5 | 6 | *Note: Due to the use of some C++ feature in header files, they can't be compiled 7 | under pure C without hacking. This will be change in future.* 8 | 9 | Please check the following example to see how to use f3kdb in your program:: 10 | 11 | // Compile with: g++ -std=c++11 -Wall -Wextra example.cpp -lf3kdb 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | int main() 18 | { 19 | static const int FRAME_WIDTH = 640; 20 | static const int FRAME_HEIGHT = 480; 21 | 22 | f3kdb_video_info_t vi; 23 | vi.width = FRAME_WIDTH; 24 | vi.height = FRAME_HEIGHT; 25 | 26 | // YUV 4:2:0 27 | vi.chroma_width_subsampling = 1; 28 | vi.chroma_height_subsampling = 1; 29 | 30 | // 8-bit video 31 | vi.pixel_mode = LOW_BIT_DEPTH; 32 | vi.depth = 8; 33 | 34 | // Set this to estimated value if frame count is unknown 35 | vi.num_frames = 42; 36 | 37 | f3kdb_params_t params; 38 | int result; 39 | result = f3kdb_params_init_defaults(¶ms); // Always call this to initialize parameters to correct default value 40 | if (result != F3KDB_SUCCESS) { 41 | printf("f3kdb_params_init_defaults code = %d\n", result); 42 | return result; 43 | } 44 | 45 | // Fill parameters. You can also directly modify params 46 | result = f3kdb_params_fill_preset(¶ms, "medium/nograin"); 47 | if (result != F3KDB_SUCCESS) { 48 | printf("f3kdb_params_fill_preset code = %d\n", result); 49 | return result; 50 | } 51 | result = f3kdb_params_fill_by_string(¶ms, "seed=42/dither_algo=2"); 52 | if (result != F3KDB_SUCCESS) { 53 | printf("f3kdb_params_fill_by_string code = %d\n", result); 54 | return result; 55 | } 56 | 57 | // No need to call f3kdb_params_sanitize or f3kdb_video_info_sanitize, 58 | // unless you want to inspect how the parameter object look like after 59 | // replacing default values with inferred values 60 | 61 | f3kdb_core_t* core; 62 | char extra_error[1024]; 63 | result = f3kdb_create(&vi, ¶ms, &core, extra_error, sizeof(extra_error)); 64 | if (result != F3KDB_SUCCESS) { 65 | printf("f3kdb_create code = %d, msg=%s\n", result, extra_error); 66 | return result; 67 | } 68 | 69 | // For demonstration purpose, we just try to deband some garbage data 70 | static const int buffer_size = FRAME_WIDTH * 480 + 15; // Keep room for 16-byte alignment 71 | unsigned char src_buffer[buffer_size]; 72 | unsigned char dst_buffer[buffer_size]; 73 | 74 | // Align the buffers 75 | // Note: Source buffer can be unaligned, but performance may be degraded for unaligned buffer 76 | // Destination buffer MUST be aligned, otherwise bad things may happen 77 | // Use posix_memalign / _aligned_malloc to allocate aligned buffer in real world 78 | unsigned char* src_buffer_ptr = (unsigned char*)(((uintptr_t)src_buffer + 15) & ~15); 79 | unsigned char* dst_buffer_ptr = (unsigned char*)(((uintptr_t)dst_buffer + 15) & ~15); 80 | 81 | // Y plane 82 | result = f3kdb_process_plane(core, 0, PLANE_Y, dst_buffer_ptr, FRAME_WIDTH, src_buffer_ptr, FRAME_WIDTH); 83 | if (result != F3KDB_SUCCESS) { 84 | printf("f3kdb_process_plane code = %d\n", result); 85 | return result; 86 | } 87 | // Cb plane 88 | result = f3kdb_process_plane(core, 0, PLANE_CB, dst_buffer_ptr, FRAME_WIDTH, src_buffer_ptr, FRAME_WIDTH); 89 | if (result != F3KDB_SUCCESS) { 90 | printf("f3kdb_process_plane code = %d\n", result); 91 | return result; 92 | } 93 | 94 | // Clean up 95 | f3kdb_destroy(core); 96 | return 0; 97 | } -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | f3kdb 2 | ===== 3 | 4 | **f3kdb** (a.k.a. flash3kyuu_deband) is a deband library and `Avisynth `_ / `Vapoursynth `_ filter, ported from `an aviutl plugin `_. 5 | 6 | It works by replacing banded pixels with average value of referenced pixels, and optionally add grain (random dithering) to them. 7 | 8 | Contents 9 | -------- 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | usage 15 | presets 16 | cpp_api_example 17 | change_log 18 | 19 | Web 20 | --- 21 | 22 | http://forum.doom9.org/showthread.php?t=161411 23 | 24 | http://www.nmm-hd.org/newbbs/viewtopic.php?f=7&t=239 25 | 26 | 27 | Acknowledgments 28 | --------------- 29 | 30 | `flash3kyuu `_ 31 | 32 | `06_taro `_ 33 | -------------------------------------------------------------------------------- /docs/source/presets.rst: -------------------------------------------------------------------------------- 1 | Available presets 2 | ----------------- 3 | 4 | ======== ============================================== 5 | Name Parameters 6 | ======== ============================================== 7 | depth y=0/cb=0/cr=0/grainy=0/grainc=0 8 | low y=32/cb=32/cr=32/grainy=32/grainc=32 9 | medium y=48/cb=48/cr=48/grainy=48/grainc=48 10 | high y=64/cb=64/cr=64/grainy=64/grainc=64 11 | veryhigh y=80/cb=80/cr=80/grainy=80/grainc=80 12 | nograin grainy=0/grainc=0 13 | luma cb=0/cr=0/grainc=0 14 | chroma y=0/grainy=0 15 | ======== ============================================== 16 | 17 | Presets can also be combined together, for example "medium/nograin" is the same as y=48/cb=48/cr=48/grainy=0/grainc=0 . -------------------------------------------------------------------------------- /docs/source/usage.rst: -------------------------------------------------------------------------------- 1 | Basic usage 2 | ----------- 3 | 4 | .. highlight:: python 5 | 6 | * Avisynth:: 7 | 8 | # Note: Only planar YUV colorspaces are supported 9 | 10 | # Load plugins, source video etc... 11 | f3kdb(...) 12 | 13 | * Vapoursynth:: 14 | 15 | import vapoursynth as vs 16 | 17 | core = vs.get_core() 18 | 19 | # Load plugins, source video etc... 20 | 21 | last = core.f3kdb.Deband(last, ...) 22 | 23 | Parameters 24 | ---------- 25 | 26 | .. option:: range 27 | 28 | Banding detection range. 29 | 30 | **Default:** 15 31 | 32 | .. option:: Y, Cb, Cr 33 | 34 | Banding detection threshold for respective plane. If difference between current pixel and 35 | reference pixel is less than threshold, it will be considered as banded. 36 | 37 | **Default:** 64 38 | 39 | .. option:: grainY, grainC 40 | 41 | Specifies amount of grains added in the last debanding stage. 42 | 43 | **Default:** 64 44 | 45 | .. option:: sample_mode 46 | 47 | Valid modes are: 48 | 49 | * 1: Take 2 pixels as reference pixel. Reference pixels are in the same 50 | column of current pixel. Add grain after processing. 51 | 52 | * 2: Take 4 pixels as reference pixel. Reference pixels are in the square 53 | around current pixel. Add grain after processing. 54 | 55 | **Default:** 2 56 | 57 | .. option:: seed 58 | 59 | Seed for random number generation. 60 | 61 | .. option:: blur_first 62 | 63 | * true: Current pixel is compared with average value of all pixels. 64 | * false: Current pixel is compared with all pixels. The pixel is considered as 65 | banded pixel only if all differences are less than threshold. 66 | 67 | **Default:** true 68 | 69 | .. option:: dynamic_grain 70 | 71 | Use different grain pattern for each frame. 72 | 73 | **Default:** false 74 | 75 | .. option:: opt 76 | 77 | Specifies optimization mode. 78 | 79 | * -1: Use highest optimization mode that is supported by host CPU 80 | 81 | * 0: No optimization (Intended for testing only) 82 | 83 | * 1: SSE2 (Pentium 4, AMD K8) 84 | 85 | * 2: SSSE3 (Core 2) 86 | 87 | * 3: SSE4.1 (Core 2 45nm) 88 | 89 | **Default:** -1 90 | 91 | .. option:: mt 92 | 93 | *Only available in Avisynth* 94 | 95 | Multi-threaded processing. If set to true, U and V plane will be proccessed 96 | in parallel with Y plane to speed up processing. 97 | 98 | **Default:** true if host has more than 1 CPU/cores, false otherwise. 99 | 100 | .. option:: dither_algo 101 | 102 | * 1: No dithering, LSB is truncated 103 | 104 | * 2: Ordered dithering 105 | 106 | * 3: Floyd-Steinberg dithering 107 | 108 | Notes: 109 | 110 | 1. Visual quality of mode 3 is the best, but the debanded pixels may 111 | easily be destroyed by x264, you need to carefully tweak the settings 112 | to get better result. 113 | 114 | 2. Mode 1 and mode 2 don't look the best, but if you are encoding at low 115 | bitrate, they may be better choice since the debanded pixels is easier 116 | to survive encoding, mode 3 may look worse than 1/2 after encoding in 117 | this situation. 118 | 119 | (Thanks sneaker_ger @ doom9 for pointing this out!) 120 | 121 | 3. This parameter is ignored if :option:`output_depth` = 16. 122 | 123 | 4. 124 | 10bit x264 command-line example:: 125 | 126 | avs2yuv -raw "script.avs" -o - | x264-10bit --demuxer raw --input-depth 16 --input-res 1280x720 --fps 24 --output "out.mp4" - 127 | 128 | Or compile x264 with the patch on https://gist.github.com/1117711, and 129 | specify the script directly:: 130 | 131 | x264-10bit --input-depth 16 --output "out.mp4" script.avs 132 | 133 | **Default:** 3 134 | 135 | .. option:: keep_tv_range 136 | 137 | If set to true, all processed pixels will be clamped to TV range 138 | (luma: 16 ~ 235, chroma: 16 ~ 240). 139 | 140 | * It is recommended to set this to true for TV-range videos, since pixel 141 | values may overflow/underflow after dithering. 142 | 143 | * DON'T set this to true for full-range videos, as all out-of-range pixels 144 | will be clamped to TV range. 145 | 146 | **Default:** false 147 | 148 | .. option:: input_mode 149 | 150 | *Removed in 2.1, since this can be inferred from clip properties* 151 | 152 | .. option:: input_depth 153 | 154 | *Removed in 2.1, since this can be inferred from clip properties* 155 | 156 | .. option:: output_mode 157 | 158 | *Removed in 2.1, f3kdb will only output high bitdepth clip in native format* 159 | 160 | .. option:: output_depth 161 | 162 | Specify output bit-depth. 163 | 164 | If :option:`output_depth` = 16, dither algorithm specified by :option:`dither_algo` won't be 165 | applied. 166 | 167 | **Range:** 8 ~ 16 168 | 169 | **Default:** 8 (:option:`output_mode` = 0 or not specified) / 16 (:option:`output_mode` = 1 or 2) 170 | 171 | .. option:: random_algo_ref, random_algo_grain 172 | 173 | Choose random number algorithm for reference positions / grains. 174 | 175 | * 0: Algorithm in old versions 176 | 177 | * 1: Uniform distribution 178 | 179 | * 2: Gaussian distribution 180 | 181 | (StdDev (sigma) is settable through :option:`random_param_ref` / :option:`random_param_grain`, 182 | Only values in [-1.0, 1.0] is used for multiplication, numbers outside 183 | this range are simply ignored) 184 | 185 | **Default:** 1 / 1 186 | 187 | .. option:: random_param_ref, random_param_grain 188 | 189 | Parameter for the respective random number generator. Currently only have 190 | effect for the Gaussian generator. 191 | 192 | **Default:** 1.0 193 | 194 | .. option:: preset 195 | 196 | Use preset parameters. Preset will be applied before other parameters so that you can easily override individual parameter. 197 | 198 | :doc:`See available presets ` 199 | -------------------------------------------------------------------------------- /include/f3kdb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "f3kdb_enums.h" 4 | #include "f3kdb_params.h" 5 | 6 | // Input plane can be unaligned, but all output planes need to be aligned to 16-byte boundary 7 | static const int PLANE_ALIGNMENT = 16; 8 | 9 | enum { 10 | // Supposed to be the same as corresponding values in avisynth 11 | PLANE_Y = 1<<0, 12 | PLANE_CB = 1<<1, 13 | PLANE_CR = 1<<2 14 | }; 15 | typedef struct _f3kdb_video_info_t 16 | { 17 | // width and height are in actual pixels, NOT pixels after applying hacks 18 | int width; 19 | int height; 20 | 21 | int chroma_width_subsampling; 22 | int chroma_height_subsampling; 23 | 24 | PIXEL_MODE pixel_mode; 25 | int depth; 26 | 27 | 28 | // Can be set to an estimated number if unknown when initializing 29 | int num_frames; 30 | 31 | inline int get_plane_width(int plane) 32 | { 33 | return plane == PLANE_Y ? width : width >> chroma_width_subsampling; 34 | } 35 | 36 | inline int get_plane_height(int plane) 37 | { 38 | return plane == PLANE_Y ? height : height >> chroma_height_subsampling; 39 | } 40 | } f3kdb_video_info_t; 41 | 42 | static const int F3KDB_INTERFACE_VERSION = 2 << 16 | sizeof(f3kdb_params_t) << 8 | sizeof(f3kdb_video_info_t); 43 | 44 | class f3kdb_core_t; 45 | 46 | enum 47 | { 48 | F3KDB_SUCCESS = 0, 49 | F3KDB_ERROR_INVALID_INTERFACE_VERSION, 50 | F3KDB_ERROR_INSUFFICIENT_MEMORY, 51 | F3KDB_ERROR_INVALID_ARGUMENT, 52 | F3KDB_ERROR_INVALID_STATE, 53 | F3KDB_ERROR_INVALID_NAME, 54 | F3KDB_ERROR_INVALID_VALUE, 55 | F3KDB_ERROR_VALUE_OUT_OF_RANGE, 56 | F3KDB_ERROR_NOT_IMPLEMENTED, 57 | F3KDB_ERROR_UNEXPECTED_END, 58 | 59 | F3KDB_ERROR_MAX 60 | }; 61 | 62 | // Stolen from VapourSynth 63 | // Convenience for C++ users. 64 | #ifdef __cplusplus 65 | # define F3KDB_EXTERN_C extern "C" 66 | #else 67 | # define F3KDB_EXTERN_C 68 | #endif 69 | 70 | #if defined(_WIN32) && !defined(_WIN64) 71 | # define F3KDB_CC __stdcall 72 | #else 73 | # define F3KDB_CC 74 | #endif 75 | 76 | // And now for some symbol hide-and-seek... 77 | #if defined(_WIN32) // Windows being special 78 | # define F3KDB_EXTERNAL_API(ret) F3KDB_EXTERN_C __declspec(dllexport) ret F3KDB_CC 79 | #elif defined(__GNUC__) && __GNUC__ >= 4 80 | # define F3KDB_EXTERNAL_API(ret) F3KDB_EXTERN_C __attribute__((visibility("default"))) ret F3KDB_CC 81 | #else 82 | # define F3KDB_EXTERNAL_API(ret) F3KDB_EXTERN_C ret F3KDB_CC 83 | #endif 84 | 85 | #if !defined(FLASH3KYUU_DEBAND_EXPORTS) && defined(_WIN32) && !defined(F3KDB_STATIC) && !defined(__GNUC__) 86 | # define F3KDB_API(ret) F3KDB_EXTERN_C __declspec(dllimport) ret F3KDB_CC 87 | #else 88 | # define F3KDB_API(ret) F3KDB_EXTERNAL_API(ret) 89 | #endif 90 | 91 | F3KDB_API(int) f3kdb_params_init_defaults(f3kdb_params_t* params, int interface_version = F3KDB_INTERFACE_VERSION); 92 | F3KDB_API(int) f3kdb_params_fill_by_string(f3kdb_params_t* params, const char* param_string, int interface_version = F3KDB_INTERFACE_VERSION); 93 | F3KDB_API(int) f3kdb_params_fill_preset(f3kdb_params_t* params, const char* preset, int interface_version = F3KDB_INTERFACE_VERSION); 94 | F3KDB_API(int) f3kdb_params_sanitize(f3kdb_params_t* params, int interface_version = F3KDB_INTERFACE_VERSION); 95 | F3KDB_API(int) f3kdb_video_info_sanitize(f3kdb_video_info_t* vi, int interface_version = F3KDB_INTERFACE_VERSION); 96 | F3KDB_API(int) f3kdb_create(const f3kdb_video_info_t* video_info, const f3kdb_params_t* params, f3kdb_core_t** core_out, char* extra_error_msg = nullptr, int error_msg_size = 0, int interface_version = F3KDB_INTERFACE_VERSION); 97 | F3KDB_API(int) f3kdb_destroy(f3kdb_core_t* core); 98 | F3KDB_API(int) f3kdb_process_plane(f3kdb_core_t* core, int frame_index, int plane, unsigned char* dst_frame_ptr, int dst_pitch, const unsigned char* src_frame_ptr, int src_pitch); 99 | -------------------------------------------------------------------------------- /include/f3kdb_enums.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef enum _PIXEL_MODE 4 | { 5 | DEFAULT_PIXEL_MODE = -1, 6 | LOW_BIT_DEPTH = 0, 7 | INVALID_OPTION1, 8 | HIGH_BIT_DEPTH_INTERLEAVED, 9 | PIXEL_MODE_COUNT 10 | } PIXEL_MODE; 11 | 12 | typedef enum _DITHER_ALGORITHM 13 | { 14 | // _DEPRECATED_DA_LOW = 0, 15 | DA_HIGH_NO_DITHERING = 1, 16 | DA_HIGH_ORDERED_DITHERING, 17 | DA_HIGH_FLOYD_STEINBERG_DITHERING, 18 | DA_16BIT_INTERLEAVED, 19 | 20 | DA_COUNT, 21 | DA_USER_PARAM_MAX = DA_HIGH_FLOYD_STEINBERG_DITHERING 22 | } DITHER_ALGORITHM; 23 | 24 | typedef enum _RANDOM_ALGORITHM { 25 | RANDOM_ALGORITHM_OLD = 0, 26 | RANDOM_ALGORITHM_UNIFORM, 27 | RANDOM_ALGORITHM_GAUSSIAN, 28 | RANDOM_ALGORITHM_COUNT 29 | } RANDOM_ALGORITHM; 30 | 31 | typedef enum _OPTIMIZATION_MODE { 32 | IMPL_AUTO_DETECT = -1, 33 | IMPL_C = 0, 34 | IMPL_SSE2, 35 | IMPL_SSSE3, 36 | IMPL_SSE4, 37 | 38 | IMPL_COUNT 39 | } OPTIMIZATION_MODE; -------------------------------------------------------------------------------- /include/f3kdb_params.h: -------------------------------------------------------------------------------- 1 | 2 | /************************* 3 | * Script generated code * 4 | * Do not modify * 5 | *************************/ 6 | 7 | #pragma once 8 | 9 | #include "f3kdb_enums.h" 10 | 11 | typedef struct _f3kdb_params_t 12 | { 13 | int range; 14 | unsigned short Y; 15 | unsigned short Cb; 16 | unsigned short Cr; 17 | int grainY; 18 | int grainC; 19 | int sample_mode; 20 | int seed; 21 | bool blur_first; 22 | bool dynamic_grain; 23 | OPTIMIZATION_MODE opt; 24 | DITHER_ALGORITHM dither_algo; 25 | bool keep_tv_range; 26 | int output_depth; 27 | RANDOM_ALGORITHM random_algo_ref; 28 | RANDOM_ALGORITHM random_algo_grain; 29 | double random_param_ref; 30 | double random_param_grain; 31 | } f3kdb_params_t; 32 | 33 | -------------------------------------------------------------------------------- /msvc/f3kdb_test.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /msvc/flash3kyuu_deband.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67a61580-c424-4c77-bab9-6bb124249721} 14 | 15 | 16 | {fe79c3ee-5a42-4400-841e-4a492c86116e} 17 | 18 | 19 | {69731926-dba0-4800-b001-068e706a9a9d} 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | avisynth 88 | 89 | 90 | avisynth 91 | 92 | 93 | include 94 | 95 | 96 | include 97 | 98 | 99 | include 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | vapoursynth 112 | 113 | 114 | Header Files 115 | 116 | 117 | vapoursynth 118 | 119 | 120 | vapoursynth 121 | 122 | 123 | vapoursynth 124 | 125 | 126 | avisynth 127 | 128 | 129 | avisynth 130 | 131 | 132 | avisynth 133 | 134 | 135 | Header Files 136 | 137 | 138 | 139 | 140 | Source Files 141 | 142 | 143 | Source Files 144 | 145 | 146 | Source Files 147 | 148 | 149 | Source Files 150 | 151 | 152 | Source Files 153 | 154 | 155 | Source Files 156 | 157 | 158 | Source Files 159 | 160 | 161 | Source Files 162 | 163 | 164 | Source Files 165 | 166 | 167 | Source Files 168 | 169 | 170 | Source Files 171 | 172 | 173 | Source Files 174 | 175 | 176 | Source Files 177 | 178 | 179 | Source Files 180 | 181 | 182 | vapoursynth 183 | 184 | 185 | avisynth 186 | 187 | 188 | -------------------------------------------------------------------------------- /msvc/wscript: -------------------------------------------------------------------------------- 1 | from waflib import Logs 2 | from waflib import Options 3 | 4 | 5 | def configure(conf): 6 | conf.load("python") 7 | conf.check_python_version((3, 0, 0)) 8 | 9 | Logs.info("Will use msbuild to compile") 10 | conf.find_program("msbuild", path_list=conf.env.PATH, var="MSBUILD") 11 | conf.env.MSBUILD = '\"%s\"' % (conf.env.MSBUILD,) 12 | 13 | if conf.options.mode not in ("debug", "release", "release_msvc"): 14 | conf.fatal("--mode must be either debug, release or release_msvc.") 15 | 16 | conf.env.BUILD_CONFIGURATION = conf.options.mode 17 | if conf.env.DEST_CPU == "x86": 18 | conf.env.TARGET_PLATFORM = "Win32" 19 | elif conf.env.DEST_CPU == "amd64": 20 | conf.env.TARGET_PLATFORM = "x64" 21 | else: 22 | conf.fatal("Unsupported target CPU: " + conf.env.DEST_CPU) 23 | 24 | conf.msg("Target platform", conf.env.TARGET_PLATFORM) 25 | 26 | conf.env.ENABLE_TEST = conf.options.test 27 | conf.msg_feature("Tests", conf.env.ENABLE_TEST) 28 | conf.env.ENABLE_AVISYNTH = conf.options.avisynth 29 | conf.msg_feature("Avisynth support", conf.env.ENABLE_AVISYNTH) 30 | 31 | 32 | def build(bld): 33 | Options.options.jobs = 1 # Don't run multiple jobs at the same time 34 | int_dir = bld.path.get_bld().make_node(r"tmp") 35 | final_output_dir = bld.path.get_bld().parent 36 | bld.env.INTDIR = int_dir.abspath() 37 | libs = [("SHARED", "DynamicLibrary", ".dll"), 38 | ("STATIC", "StaticLibrary", ".lib")] 39 | bld.env.DISABLE_VAPOURSYNTH = str(not bld.env.ENABLE_VAPOURSYNTH).lower() 40 | bld.env.DISABLE_AVISYNTH = str(not bld.env.ENABLE_AVISYNTH).lower() 41 | for var, type, ext in libs: 42 | if not bld.env[var]: 43 | continue 44 | 45 | output_name = bld.env.APPNAME + ext 46 | int_output_dir = int_dir.find_or_declare(var.lower()) 47 | int_output = int_output_dir.find_or_declare(output_name) 48 | msbuild_params = ( 49 | r"/t:Build " 50 | r"/p:OutDir=" + int_output_dir.abspath() + r"\ " 51 | r"/p:Configuration=${BUILD_CONFIGURATION} " 52 | r"/p:Platform=${TARGET_PLATFORM} " 53 | r"/p:DisableVapoursynth=${DISABLE_VAPOURSYNTH} " 54 | r"/p:DisableAvisynth=${DISABLE_AVISYNTH} " 55 | r"/p:BuildProjectReferences=false " 56 | r"/p:DisableProjectReferences=true " 57 | ) 58 | bld( 59 | rule=( 60 | "${MSBUILD} ${SRC[0].abspath()} " 61 | r"/p:TargetName=${APPNAME} " 62 | r"/p:IntDir=${INTDIR}\obj\ " 63 | r"/p:ConfigurationType=" + type + " " 64 | + msbuild_params 65 | ), 66 | source=bld.path.find_resource("flash3kyuu_deband.vcxproj"), 67 | target=int_output, 68 | always=True, 69 | update_outputs=True, 70 | ) 71 | bld( 72 | rule="copy \"${SRC}\" \"${TGT}\"", 73 | source=int_output, 74 | target=final_output_dir.find_or_declare(output_name), 75 | ) 76 | 77 | if bld.env.ENABLE_TEST: 78 | test_output_name = bld.env.APPNAME + "-test.exe" 79 | test_int_output = int_output_dir.find_or_declare(test_output_name) 80 | bld( 81 | rule=( 82 | "${MSBUILD} ${SRC[0].abspath()} " 83 | r"/p:TargetName=${APPNAME}-test " 84 | r"/p:IntDir=${INTDIR}\obj\test\ " 85 | r"/p:AdditionalDependencies=${APPNAME}.lib " 86 | + ("/p:StaticF3kdb=true " if type == "StaticLibrary" else "") 87 | + msbuild_params 88 | ), 89 | source=[ 90 | bld.path.find_resource("f3kdb_test.vcxproj"), 91 | int_output, 92 | ], 93 | target=test_int_output, 94 | always=True, 95 | update_outputs=True, 96 | ) 97 | bld( 98 | rule="copy \"${SRC}\" \"${TGT}\"", 99 | source=test_int_output, 100 | target=final_output_dir.find_or_declare(test_output_name), 101 | ) 102 | 103 | -------------------------------------------------------------------------------- /src/auto_utils.cpp: -------------------------------------------------------------------------------- 1 | 2 | /************************* 3 | * Script generated code * 4 | * Do not modify * 5 | *************************/ 6 | 7 | #include 8 | 9 | #include "random.h" 10 | #include "auto_utils.h" 11 | #include "auto_utils_helper.h" 12 | 13 | void params_set_defaults(f3kdb_params_t* params) 14 | { 15 | params->range = 15; 16 | params->Y = 64; 17 | params->Cb = 64; 18 | params->Cr = 64; 19 | params->grainY = 64; 20 | params->grainC = 64; 21 | params->sample_mode = 2; 22 | params->seed = 0; 23 | params->blur_first = true; 24 | params->dynamic_grain = false; 25 | params->opt = IMPL_AUTO_DETECT; 26 | params->dither_algo = DA_HIGH_FLOYD_STEINBERG_DITHERING; 27 | params->keep_tv_range = false; 28 | params->output_depth = -1; 29 | params->random_algo_ref = RANDOM_ALGORITHM_UNIFORM; 30 | params->random_algo_grain = RANDOM_ALGORITHM_UNIFORM; 31 | params->random_param_ref = DEFAULT_RANDOM_PARAM; 32 | params->random_param_grain = DEFAULT_RANDOM_PARAM; 33 | } 34 | 35 | int params_set_by_string(f3kdb_params_t* params, const char* name, const char* value_string) 36 | { 37 | if (!_stricmp(name, "range")) { return params_set_value_by_string(¶ms->range, value_string); } 38 | if (!_stricmp(name, "Y")) { return params_set_value_by_string(¶ms->Y, value_string); } 39 | if (!_stricmp(name, "Cb")) { return params_set_value_by_string(¶ms->Cb, value_string); } 40 | if (!_stricmp(name, "Cr")) { return params_set_value_by_string(¶ms->Cr, value_string); } 41 | if (!_stricmp(name, "grainY")) { return params_set_value_by_string(¶ms->grainY, value_string); } 42 | if (!_stricmp(name, "grainC")) { return params_set_value_by_string(¶ms->grainC, value_string); } 43 | if (!_stricmp(name, "sample_mode")) { return params_set_value_by_string(¶ms->sample_mode, value_string); } 44 | if (!_stricmp(name, "seed")) { return params_set_value_by_string(¶ms->seed, value_string); } 45 | if (!_stricmp(name, "blur_first")) { return params_set_value_by_string(¶ms->blur_first, value_string); } 46 | if (!_stricmp(name, "dynamic_grain")) { return params_set_value_by_string(¶ms->dynamic_grain, value_string); } 47 | if (!_stricmp(name, "opt")) { return params_set_value_by_string(¶ms->opt, value_string); } 48 | if (!_stricmp(name, "dither_algo")) { return params_set_value_by_string(¶ms->dither_algo, value_string); } 49 | if (!_stricmp(name, "keep_tv_range")) { return params_set_value_by_string(¶ms->keep_tv_range, value_string); } 50 | if (!_stricmp(name, "output_depth")) { return params_set_value_by_string(¶ms->output_depth, value_string); } 51 | if (!_stricmp(name, "random_algo_ref")) { return params_set_value_by_string(¶ms->random_algo_ref, value_string); } 52 | if (!_stricmp(name, "random_algo_grain")) { return params_set_value_by_string(¶ms->random_algo_grain, value_string); } 53 | if (!_stricmp(name, "random_param_ref")) { return params_set_value_by_string(¶ms->random_param_ref, value_string); } 54 | if (!_stricmp(name, "random_param_grain")) { return params_set_value_by_string(¶ms->random_param_grain, value_string); } 55 | return F3KDB_ERROR_INVALID_NAME; 56 | } 57 | -------------------------------------------------------------------------------- /src/auto_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "compiler_compat.h" 5 | 6 | void params_set_defaults(f3kdb_params_t* params); 7 | 8 | int params_set_by_string(f3kdb_params_t* params, const char* name, const char* value_string); 9 | -------------------------------------------------------------------------------- /src/auto_utils_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | using namespace std; 12 | 13 | template 14 | struct number_converter 15 | { 16 | typedef long intermediate_type; 17 | static intermediate_type convert(const char* value_string, char** end) 18 | { 19 | return strtol(value_string, end, 10); 20 | } 21 | }; 22 | 23 | template<> 24 | struct number_converter 25 | { 26 | typedef double intermediate_type; 27 | static intermediate_type convert(const char* value_string, char** end) 28 | { 29 | return strtod(value_string, end); 30 | } 31 | }; 32 | 33 | template 34 | int params_set_value_by_string(T* target, const char* value_string) 35 | { 36 | static_assert(is_integral::value || is_same::value || is_enum::value, "T must be integral type"); 37 | char* end = NULL; 38 | errno = 0; 39 | typename number_converter::intermediate_type value; 40 | value = number_converter::convert(value_string, &end); 41 | if (errno == ERANGE) 42 | { 43 | return F3KDB_ERROR_VALUE_OUT_OF_RANGE; 44 | } 45 | if (end != value_string + strlen(value_string)) 46 | { 47 | return F3KDB_ERROR_INVALID_VALUE; 48 | } 49 | if (is_integral::value) 50 | { 51 | if (value < numeric_limits::min() || value > numeric_limits::max()) 52 | { 53 | return F3KDB_ERROR_VALUE_OUT_OF_RANGE; 54 | } 55 | } 56 | *target = (T)value; 57 | return F3KDB_SUCCESS; 58 | } 59 | 60 | bool any_equals(const char* comparand, ...) 61 | { 62 | va_list va; 63 | va_start(va, comparand); 64 | const char* str = va_arg(va, const char*); 65 | while (str) 66 | { 67 | if (!_stricmp(comparand, str)) 68 | { 69 | va_end(va); 70 | return true; 71 | } 72 | str = va_arg(va, const char*); 73 | } 74 | va_end(va); 75 | return false; 76 | } 77 | 78 | template<> 79 | int params_set_value_by_string(bool* target, const char* value_string) 80 | { 81 | if (any_equals(value_string, "true", "t", "yes", "y", "1", NULL)) 82 | { 83 | *target = true; 84 | return F3KDB_SUCCESS; 85 | } 86 | if (any_equals(value_string, "false", "f", "no", "n", "0", NULL)) 87 | { 88 | *target = false; 89 | return F3KDB_SUCCESS; 90 | } 91 | return F3KDB_ERROR_INVALID_VALUE; 92 | } -------------------------------------------------------------------------------- /src/avisynth/avisynth_plugin.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/src/avisynth/avisynth_plugin.hpp -------------------------------------------------------------------------------- /src/avisynth/avisynth_plugin_v6.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "avisynth.h" 4 | 5 | #include "../compiler_compat.h" 6 | #include "filter_impl.hpp" 7 | 8 | const AVS_Linkage *AVS_linkage; 9 | F3KDB_API(const char*) AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) { 10 | AVS_linkage = vectors; 11 | env->AddFunction("flash3kyuu_deband", 12 | F3KDB_AVS_PARAMS, 13 | Create_flash3kyuu_deband, 14 | NULL); 15 | env->AddFunction("f3kdb", 16 | F3KDB_AVS_PARAMS, 17 | Create_flash3kyuu_deband, 18 | NULL); 19 | 20 | return "f3kdb"; 21 | } -------------------------------------------------------------------------------- /src/avisynth/avs/alignment.h: -------------------------------------------------------------------------------- 1 | // Avisynth C Interface Version 0.20 2 | // Copyright 2003 Kevin Atkinson 3 | 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 17 | // http://www.gnu.org/copyleft/gpl.html . 18 | // 19 | // As a special exception, I give you permission to link to the 20 | // Avisynth C interface with independent modules that communicate with 21 | // the Avisynth C interface solely through the interfaces defined in 22 | // avisynth_c.h, regardless of the license terms of these independent 23 | // modules, and to copy and distribute the resulting combined work 24 | // under terms of your choice, provided that every copy of the 25 | // combined work is accompanied by a complete copy of the source code 26 | // of the Avisynth C interface and Avisynth itself (with the version 27 | // used to produce the combined work), being distributed under the 28 | // terms of the GNU General Public License plus this exception. An 29 | // independent module is a module which is not derived from or based 30 | // on Avisynth C Interface, such as 3rd-party filters, import and 31 | // export plugins, or graphical user interfaces. 32 | 33 | #ifndef AVS_ALIGNMENT_H 34 | #define AVS_ALIGNMENT_H 35 | 36 | // Functions and macros to help work with alignment requirements. 37 | 38 | // Tells if a number is a power of two. 39 | #define IS_POWER2(n) ((n) && !((n) & ((n) - 1))) 40 | 41 | // Tells if the pointer "ptr" is aligned to "align" bytes. 42 | #define IS_PTR_ALIGNED(ptr, align) (((uintptr_t)ptr & ((uintptr_t)(align-1))) == 0) 43 | 44 | // Rounds up the number "n" to the next greater multiple of "align" 45 | #define ALIGN_NUMBER(n, align) (((n) + (align)-1) & (~((align)-1))) 46 | 47 | // Rounds up the pointer address "ptr" to the next greater multiple of "align" 48 | #define ALIGN_POINTER(ptr, align) (((uintptr_t)(ptr) + (align)-1) & (~(uintptr_t)((align)-1))) 49 | 50 | #ifdef __cplusplus 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #if defined(MSVC) 58 | // needed for VS2013, otherwise C++11 'alignas' works 59 | #define avs_alignas(x) __declspec(align(x)) 60 | #else 61 | // assumes C++11 support 62 | #define avs_alignas(x) alignas(x) 63 | #endif 64 | 65 | template 66 | static bool IsPtrAligned(T* ptr, size_t align) 67 | { 68 | assert(IS_POWER2(align)); 69 | return (bool)IS_PTR_ALIGNED(ptr, align); 70 | } 71 | 72 | template 73 | static T AlignNumber(T n, T align) 74 | { 75 | assert(IS_POWER2(align)); 76 | return ALIGN_NUMBER(n, align); 77 | } 78 | 79 | template 80 | static T* AlignPointer(T* ptr, size_t align) 81 | { 82 | assert(IS_POWER2(align)); 83 | return (T*)ALIGN_POINTER(ptr, align); 84 | } 85 | 86 | extern "C" 87 | { 88 | #else 89 | #include 90 | #endif // __cplusplus 91 | 92 | // Returns a new buffer that is at least the size "nbytes". 93 | // The buffer will be aligned to "align" bytes. 94 | // Returns NULL on error. On successful allocation, 95 | // the returned buffer must be freed using "avs_free". 96 | inline void* avs_malloc(size_t nbytes, size_t align) 97 | { 98 | if (!IS_POWER2(align)) 99 | return NULL; 100 | 101 | size_t offset = sizeof(void*) + align - 1; 102 | 103 | void *orig = malloc(nbytes + offset); 104 | if (orig == NULL) 105 | return NULL; 106 | 107 | void **aligned = (void**)(((uintptr_t)orig + (uintptr_t)offset) & (~(uintptr_t)(align-1))); 108 | aligned[-1] = orig; 109 | return aligned; 110 | } 111 | 112 | // Buffers allocated using "avs_malloc" must be freed 113 | // using "avs_free" instead of "free". 114 | inline void avs_free(void *ptr) 115 | { 116 | // Mirroring free()'s semantic requires us to accept NULLs 117 | if (ptr == NULL) 118 | return; 119 | 120 | free(((void**)ptr)[-1]); 121 | } 122 | 123 | #ifdef __cplusplus 124 | } // extern "C" 125 | 126 | // The point of these undef's is to force using the template functions 127 | // if we are in C++ mode. For C, the user can rely only on the macros. 128 | #undef IS_PTR_ALIGNED 129 | #undef ALIGN_NUMBER 130 | #undef ALIGN_POINTER 131 | 132 | #endif // __cplusplus 133 | 134 | #endif //AVS_ALIGNMENT_H 135 | -------------------------------------------------------------------------------- /src/avisynth/avs/capi.h: -------------------------------------------------------------------------------- 1 | // Avisynth C Interface Version 0.20 2 | // Copyright 2003 Kevin Atkinson 3 | 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 17 | // http://www.gnu.org/copyleft/gpl.html . 18 | // 19 | // As a special exception, I give you permission to link to the 20 | // Avisynth C interface with independent modules that communicate with 21 | // the Avisynth C interface solely through the interfaces defined in 22 | // avisynth_c.h, regardless of the license terms of these independent 23 | // modules, and to copy and distribute the resulting combined work 24 | // under terms of your choice, provided that every copy of the 25 | // combined work is accompanied by a complete copy of the source code 26 | // of the Avisynth C interface and Avisynth itself (with the version 27 | // used to produce the combined work), being distributed under the 28 | // terms of the GNU General Public License plus this exception. An 29 | // independent module is a module which is not derived from or based 30 | // on Avisynth C Interface, such as 3rd-party filters, import and 31 | // export plugins, or graphical user interfaces. 32 | 33 | #ifndef AVS_CAPI_H 34 | #define AVS_CAPI_H 35 | 36 | #ifdef __cplusplus 37 | # define EXTERN_C extern "C" 38 | #else 39 | # define EXTERN_C 40 | #endif 41 | 42 | #ifdef MSVC 43 | #ifndef AVSC_USE_STDCALL 44 | # define AVSC_CC __cdecl 45 | #else 46 | # define AVSC_CC __stdcall 47 | #endif 48 | #else 49 | # define AVSC_CC 50 | #endif 51 | 52 | #define AVSC_INLINE static __inline 53 | 54 | #ifdef BUILDING_AVSCORE 55 | # define AVSC_EXPORT __declspec(dllexport) 56 | # define AVSC_API(ret, name) EXTERN_C AVSC_EXPORT ret AVSC_CC name 57 | #else 58 | # define AVSC_EXPORT EXTERN_C __declspec(dllexport) 59 | # ifndef AVSC_NO_DECLSPEC 60 | # define AVSC_API(ret, name) EXTERN_C __declspec(dllimport) ret AVSC_CC name 61 | # else 62 | # define AVSC_API(ret, name) typedef ret (AVSC_CC *name##_func) 63 | # endif 64 | #endif 65 | 66 | #endif //AVS_CAPI_H 67 | -------------------------------------------------------------------------------- /src/avisynth/avs/config.h: -------------------------------------------------------------------------------- 1 | // Avisynth C Interface Version 0.20 2 | // Copyright 2003 Kevin Atkinson 3 | 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 17 | // http://www.gnu.org/copyleft/gpl.html . 18 | // 19 | // As a special exception, I give you permission to link to the 20 | // Avisynth C interface with independent modules that communicate with 21 | // the Avisynth C interface solely through the interfaces defined in 22 | // avisynth_c.h, regardless of the license terms of these independent 23 | // modules, and to copy and distribute the resulting combined work 24 | // under terms of your choice, provided that every copy of the 25 | // combined work is accompanied by a complete copy of the source code 26 | // of the Avisynth C interface and Avisynth itself (with the version 27 | // used to produce the combined work), being distributed under the 28 | // terms of the GNU General Public License plus this exception. An 29 | // independent module is a module which is not derived from or based 30 | // on Avisynth C Interface, such as 3rd-party filters, import and 31 | // export plugins, or graphical user interfaces. 32 | 33 | #ifndef AVS_CONFIG_H 34 | #define AVS_CONFIG_H 35 | 36 | // Undefine this to get cdecl calling convention 37 | #define AVSC_USE_STDCALL 1 38 | 39 | // NOTE TO PLUGIN AUTHORS: 40 | // Because FRAME_ALIGN can be substantially higher than the alignment 41 | // a plugin actually needs, plugins should not use FRAME_ALIGN to check for 42 | // alignment. They should always request the exact alignment value they need. 43 | // This is to make sure that plugins work over the widest range of AviSynth 44 | // builds possible. 45 | #define FRAME_ALIGN 64 46 | 47 | #if defined(_M_AMD64) || defined(__x86_64) 48 | # define X86_64 49 | #elif defined(_M_IX86) || defined(__i386__) 50 | # define X86_32 51 | #else 52 | # error Unsupported CPU architecture. 53 | #endif 54 | 55 | #if defined(_MSC_VER) 56 | # define MSVC 57 | #elif defined(__GNUC__) 58 | # define GCC 59 | #elif defined(__clang__) 60 | # define CLANG 61 | #else 62 | # error Unsupported compiler. 63 | #endif 64 | 65 | #if defined(GCC) 66 | # undef __forceinline 67 | # define __forceinline inline 68 | #endif 69 | 70 | #endif //AVS_CONFIG_H 71 | -------------------------------------------------------------------------------- /src/avisynth/avs/cpuid.h: -------------------------------------------------------------------------------- 1 | // This program is free software; you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation; either version 2 of the License, or 4 | // (at your option) any later version. 5 | // 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | // 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program; if not, write to the Free Software 13 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 14 | // http://www.gnu.org/copyleft/gpl.html . 15 | // 16 | // Linking Avisynth statically or dynamically with other modules is making a 17 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU 18 | // General Public License cover the whole combination. 19 | // 20 | // As a special exception, the copyright holders of Avisynth give you 21 | // permission to link Avisynth with independent modules that communicate with 22 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license 23 | // terms of these independent modules, and to copy and distribute the 24 | // resulting combined work under terms of your choice, provided that 25 | // every copy of the combined work is accompanied by a complete copy of 26 | // the source code of Avisynth (the version of Avisynth used to produce the 27 | // combined work), being distributed under the terms of the GNU General 28 | // Public License plus this exception. An independent module is a module 29 | // which is not derived from or based on Avisynth, such as 3rd-party filters, 30 | // import and export plugins, or graphical user interfaces. 31 | 32 | #ifndef AVSCORE_CPUID_H 33 | #define AVSCORE_CPUID_H 34 | 35 | // For GetCPUFlags. These are backwards-compatible with those in VirtualDub. 36 | // ending with SSE4_2 37 | // For emulation see https://software.intel.com/en-us/articles/intel-software-development-emulator 38 | enum { 39 | /* oldest CPU to support extension */ 40 | CPUF_FORCE = 0x01, // N/A 41 | CPUF_FPU = 0x02, // 386/486DX 42 | CPUF_MMX = 0x04, // P55C, K6, PII 43 | CPUF_INTEGER_SSE = 0x08, // PIII, Athlon 44 | CPUF_SSE = 0x10, // PIII, Athlon XP/MP 45 | CPUF_SSE2 = 0x20, // PIV, K8 46 | CPUF_3DNOW = 0x40, // K6-2 47 | CPUF_3DNOW_EXT = 0x80, // Athlon 48 | CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which 49 | // only Hammer will have anyway) 50 | CPUF_SSE3 = 0x100, // PIV+, K8 Venice 51 | CPUF_SSSE3 = 0x200, // Core 2 52 | CPUF_SSE4 = 0x400, 53 | CPUF_SSE4_1 = 0x400, // Penryn, Wolfdale, Yorkfield 54 | CPUF_AVX = 0x800, // Sandy Bridge, Bulldozer 55 | CPUF_SSE4_2 = 0x1000, // Nehalem 56 | // AVS+ 57 | CPUF_AVX2 = 0x2000, // Haswell 58 | CPUF_FMA3 = 0x4000, 59 | CPUF_F16C = 0x8000, 60 | CPUF_MOVBE = 0x10000, // Big Endian move 61 | CPUF_POPCNT = 0x20000, 62 | CPUF_AES = 0x40000, 63 | CPUF_FMA4 = 0x80000, 64 | 65 | CPUF_AVX512F = 0x100000, // AVX-512 Foundation. 66 | CPUF_AVX512DQ = 0x200000, // AVX-512 DQ (Double/Quad granular) Instructions 67 | CPUF_AVX512PF = 0x400000, // AVX-512 Prefetch 68 | CPUF_AVX512ER = 0x800000, // AVX-512 Exponential and Reciprocal 69 | CPUF_AVX512CD = 0x1000000, // AVX-512 Conflict Detection 70 | CPUF_AVX512BW = 0x2000000, // AVX-512 BW (Byte/Word granular) Instructions 71 | CPUF_AVX512VL = 0x4000000, // AVX-512 VL (128/256 Vector Length) Extensions 72 | CPUF_AVX512IFMA = 0x8000000, // AVX-512 IFMA integer 52 bit 73 | CPUF_AVX512VBMI = 0x10000000,// AVX-512 VBMI 74 | }; 75 | 76 | #ifdef BUILDING_AVSCORE 77 | int GetCPUFlags(); 78 | #endif 79 | 80 | #endif // AVSCORE_CPUID_H 81 | -------------------------------------------------------------------------------- /src/avisynth/avs/minmax.h: -------------------------------------------------------------------------------- 1 | // This program is free software; you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation; either version 2 of the License, or 4 | // (at your option) any later version. 5 | // 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | // 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program; if not, write to the Free Software 13 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 14 | // http://www.gnu.org/copyleft/gpl.html . 15 | // 16 | // Linking Avisynth statically or dynamically with other modules is making a 17 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU 18 | // General Public License cover the whole combination. 19 | // 20 | // As a special exception, the copyright holders of Avisynth give you 21 | // permission to link Avisynth with independent modules that communicate with 22 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license 23 | // terms of these independent modules, and to copy and distribute the 24 | // resulting combined work under terms of your choice, provided that 25 | // every copy of the combined work is accompanied by a complete copy of 26 | // the source code of Avisynth (the version of Avisynth used to produce the 27 | // combined work), being distributed under the terms of the GNU General 28 | // Public License plus this exception. An independent module is a module 29 | // which is not derived from or based on Avisynth, such as 3rd-party filters, 30 | // import and export plugins, or graphical user interfaces. 31 | 32 | #ifndef AVSCORE_MINMAX_H 33 | #define AVSCORE_MINMAX_H 34 | 35 | template 36 | T min(T v1, T v2) 37 | { 38 | return v1 < v2 ? v1 : v2; 39 | } 40 | 41 | template 42 | T max(T v1, T v2) 43 | { 44 | return v1 > v2 ? v1 : v2; 45 | } 46 | 47 | template 48 | T clamp(T n, T min, T max) 49 | { 50 | n = n > max ? max : n; 51 | return n < min ? min : n; 52 | } 53 | 54 | #endif // AVSCORE_MINMAX_H 55 | -------------------------------------------------------------------------------- /src/avisynth/avs/types.h: -------------------------------------------------------------------------------- 1 | // Avisynth C Interface Version 0.20 2 | // Copyright 2003 Kevin Atkinson 3 | 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 17 | // http://www.gnu.org/copyleft/gpl.html . 18 | // 19 | // As a special exception, I give you permission to link to the 20 | // Avisynth C interface with independent modules that communicate with 21 | // the Avisynth C interface solely through the interfaces defined in 22 | // avisynth_c.h, regardless of the license terms of these independent 23 | // modules, and to copy and distribute the resulting combined work 24 | // under terms of your choice, provided that every copy of the 25 | // combined work is accompanied by a complete copy of the source code 26 | // of the Avisynth C interface and Avisynth itself (with the version 27 | // used to produce the combined work), being distributed under the 28 | // terms of the GNU General Public License plus this exception. An 29 | // independent module is a module which is not derived from or based 30 | // on Avisynth C Interface, such as 3rd-party filters, import and 31 | // export plugins, or graphical user interfaces. 32 | 33 | #ifndef AVS_TYPES_H 34 | #define AVS_TYPES_H 35 | 36 | // Define all types necessary for interfacing with avisynth.dll 37 | 38 | #ifdef __cplusplus 39 | #include 40 | #else 41 | #include 42 | #endif 43 | 44 | // Raster types used by VirtualDub & Avisynth 45 | typedef unsigned int Pixel32; 46 | typedef unsigned char BYTE; 47 | 48 | // Audio Sample information 49 | typedef float SFLOAT; 50 | 51 | #ifdef __GNUC__ 52 | typedef long long int INT64; 53 | #else 54 | typedef __int64 INT64; 55 | #endif 56 | 57 | #endif //AVS_TYPES_H 58 | -------------------------------------------------------------------------------- /src/avisynth/avs/win.h: -------------------------------------------------------------------------------- 1 | // This program is free software; you can redistribute it and/or modify 2 | // it under the terms of the GNU General Public License as published by 3 | // the Free Software Foundation; either version 2 of the License, or 4 | // (at your option) any later version. 5 | // 6 | // This program is distributed in the hope that it will be useful, 7 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | // GNU General Public License for more details. 10 | // 11 | // You should have received a copy of the GNU General Public License 12 | // along with this program; if not, write to the Free Software 13 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit 14 | // http://www.gnu.org/copyleft/gpl.html . 15 | // 16 | // Linking Avisynth statically or dynamically with other modules is making a 17 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU 18 | // General Public License cover the whole combination. 19 | // 20 | // As a special exception, the copyright holders of Avisynth give you 21 | // permission to link Avisynth with independent modules that communicate with 22 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license 23 | // terms of these independent modules, and to copy and distribute the 24 | // resulting combined work under terms of your choice, provided that 25 | // every copy of the combined work is accompanied by a complete copy of 26 | // the source code of Avisynth (the version of Avisynth used to produce the 27 | // combined work), being distributed under the terms of the GNU General 28 | // Public License plus this exception. An independent module is a module 29 | // which is not derived from or based on Avisynth, such as 3rd-party filters, 30 | // import and export plugins, or graphical user interfaces. 31 | 32 | #ifndef AVSCORE_WIN_H 33 | #define AVSCORE_WIN_H 34 | 35 | // Whenever you need windows headers, start by including this file, then the rest. 36 | 37 | // WWUUT? We require XP now? 38 | #if !defined(NTDDI_VERSION) && !defined(_WIN32_WINNT) 39 | #define NTDDI_VERSION 0x05020000 40 | #define _WIN32_WINNT 0x0502 41 | #endif 42 | 43 | #define WIN32_LEAN_AND_MEAN 44 | #define STRICT 45 | #if !defined(NOMINMAX) 46 | #define NOMINMAX 47 | #endif 48 | 49 | #include 50 | 51 | // Provision for UTF-8 max 4 bytes per code point 52 | #define AVS_MAX_PATH MAX_PATH*4 53 | 54 | #endif // AVSCORE_WIN_H 55 | -------------------------------------------------------------------------------- /src/avisynth/check.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "avisynth.h" 4 | 5 | #include 6 | 7 | static void check_parameter_range(const char* name, int value, int lower_bound, int upper_bound, char* param_name, IScriptEnvironment* env) { 8 | if (value < lower_bound || value > upper_bound) { 9 | char msg[1024]; 10 | sprintf_s(msg, "%s: Invalid value for parameter %s: %d, must be %d ~ %d.", 11 | name, param_name, value, lower_bound, upper_bound); 12 | env->ThrowError(_strdup(msg)); 13 | } 14 | } 15 | 16 | static void check_video_format(const char* name, const VideoInfo& vi, IScriptEnvironment* env) 17 | { 18 | char name_buffer[256]; 19 | if (!vi.IsYUV() || !vi.IsPlanar()) { 20 | sprintf_s(name_buffer, "%s: Only planar YUV clips are supported.", name); 21 | env->ThrowError(_strdup(name_buffer)); 22 | } 23 | if (vi.IsFieldBased()) { 24 | sprintf_s(name_buffer, "%s: Field-based clip is not supported.", name); 25 | env->ThrowError(_strdup(name_buffer)); 26 | } 27 | } -------------------------------------------------------------------------------- /src/avisynth/filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "avisynth.h" 5 | #include "flash3kyuu_deband.def.h" 6 | 7 | #include "mt_info.hpp" 8 | 9 | AVSValue __cdecl Create_flash3kyuu_deband(AVSValue args, void* user_data, IScriptEnvironment* env); 10 | 11 | class f3kdb_avisynth : public GenericVideoFilter { 12 | private: 13 | f3kdb_core_t* _core; 14 | bool _mt; 15 | volatile mt_info* _mt_info; 16 | VideoInfo output_vi; 17 | 18 | void process_plane(int n, const PVideoFrame& src, const PVideoFrame& dst, unsigned char *dstp, int plane, IScriptEnvironment* env); 19 | 20 | public: 21 | void mt_proc(void); 22 | f3kdb_avisynth(PClip child, f3kdb_core_t* core, VideoInfo output_vi, bool mt); 23 | ~f3kdb_avisynth(); 24 | 25 | PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); 26 | }; -------------------------------------------------------------------------------- /src/avisynth/filter_impl.hpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define F3KDB_SIMPLE_MACRO_NAME 10 | 11 | #include "filter.h" 12 | #include "check.hpp" 13 | 14 | AVSValue __cdecl Create_flash3kyuu_deband(AVSValue args, void* user_data, IScriptEnvironment* env){ 15 | PClip child = ARG(child).AsClip(); 16 | const VideoInfo& vi = child->GetVideoInfo(); 17 | check_video_format("f3kdb", vi, env); 18 | 19 | SYSTEM_INFO si; 20 | memset(&si, 0, sizeof(si)); 21 | GetSystemInfo(&si); 22 | bool mt = ARG(mt).AsBool(si.dwNumberOfProcessors > 1); 23 | 24 | f3kdb_params_t params; 25 | f3kdb_params_init_defaults(¶ms); 26 | 27 | if (F3KDB_ARG(preset).Defined()) { 28 | int result = f3kdb_params_fill_preset(¶ms, F3KDB_ARG(preset).AsString()); 29 | if (result != F3KDB_SUCCESS) { 30 | env->ThrowError("f3kdb: Invalid preset (code: %d)", result); 31 | } 32 | } 33 | 34 | f3kdb_params_from_avs(args, ¶ms); 35 | if (params.output_depth == -1) 36 | params.output_depth = vi.BitsPerComponent(); 37 | 38 | f3kdb_video_info_t video_info; 39 | video_info.num_frames = vi.num_frames; 40 | video_info.pixel_mode = vi.BitsPerComponent() == 8 ? LOW_BIT_DEPTH : HIGH_BIT_DEPTH_INTERLEAVED; 41 | video_info.depth = vi.BitsPerComponent(); 42 | video_info.chroma_width_subsampling = vi.IsY8() ? 0 : vi.GetPlaneWidthSubsampling(PLANAR_U); 43 | video_info.chroma_height_subsampling = vi.IsY8() ? 0 : vi.GetPlaneHeightSubsampling(PLANAR_U); 44 | video_info.width = vi.width; 45 | video_info.height = vi.height; 46 | 47 | f3kdb_core_t* core = NULL; 48 | char error_msg[1024]; 49 | memset(error_msg, 0, sizeof(error_msg)); 50 | int result = f3kdb_create(&video_info, ¶ms, &core, error_msg, sizeof(error_msg) - 1); 51 | if (result != F3KDB_SUCCESS) 52 | { 53 | env->ThrowError("f3kdb: Initialization failed (code: %d). %s", result, error_msg); 54 | } 55 | VideoInfo output_vi = vi; 56 | if (output_vi.pixel_type == VideoInfo::CS_I420) 57 | output_vi.pixel_type = VideoInfo::CS_YV12; 58 | output_vi.pixel_type &= ~VideoInfo::CS_Sample_Bits_Mask; 59 | switch (params.output_depth) { 60 | case 8: 61 | output_vi.pixel_type |= VideoInfo::CS_Sample_Bits_8; 62 | break; 63 | case 10: 64 | output_vi.pixel_type |= VideoInfo::CS_Sample_Bits_10; 65 | break; 66 | case 12: 67 | output_vi.pixel_type |= VideoInfo::CS_Sample_Bits_12; 68 | break; 69 | case 14: 70 | output_vi.pixel_type |= VideoInfo::CS_Sample_Bits_14; 71 | break; 72 | case 16: 73 | output_vi.pixel_type |= VideoInfo::CS_Sample_Bits_16; 74 | break; 75 | default: 76 | env->ThrowError("f3kdb: Unsupported output depth (%d).", params.output_depth); 77 | break; 78 | } 79 | 80 | return new f3kdb_avisynth(child, core, output_vi, mt); 81 | } 82 | f3kdb_avisynth::f3kdb_avisynth(PClip child, f3kdb_core_t* core, VideoInfo output_vi, bool mt) : 83 | GenericVideoFilter(child), 84 | _mt_info(NULL), _mt(mt), 85 | _core(core) 86 | { 87 | vi.width = output_vi.width; 88 | vi.height = output_vi.height; 89 | vi.pixel_type = output_vi.pixel_type; 90 | } 91 | 92 | f3kdb_avisynth::~f3kdb_avisynth() 93 | { 94 | mt_info_destroy(_mt_info); 95 | _mt_info = NULL; 96 | f3kdb_destroy(_core); 97 | _core = NULL; 98 | } 99 | 100 | void f3kdb_avisynth::process_plane(int n, const PVideoFrame& src, const PVideoFrame& dst, unsigned char *dstp, int plane, IScriptEnvironment* env) 101 | { 102 | int f3kdb_plane; 103 | switch (plane & 7) 104 | { 105 | case PLANAR_Y: 106 | f3kdb_plane = PLANE_Y; 107 | break; 108 | case PLANAR_U: 109 | f3kdb_plane = PLANE_CB; 110 | break; 111 | case PLANAR_V: 112 | f3kdb_plane = PLANE_CR; 113 | break; 114 | default: 115 | assert(false); 116 | env->ThrowError("f3kdb: Invalid plane"); 117 | } 118 | int result = f3kdb_process_plane(_core, n, f3kdb_plane, dstp, dst->GetPitch(plane), src->GetReadPtr(plane), src->GetPitch(plane)); 119 | if (result != F3KDB_SUCCESS) 120 | { 121 | env->ThrowError("f3kdb: Unknown error, code = %d", result); 122 | } 123 | } 124 | 125 | void f3kdb_avisynth::mt_proc(void) 126 | { 127 | volatile mt_info* info = _mt_info; 128 | assert(info); 129 | 130 | while (!info->exit) 131 | { 132 | MemoryBarrier(); 133 | { 134 | auto info_nv = const_cast(info); 135 | process_plane(info_nv->n, info_nv->src, info_nv->dst, info_nv->dstp_u, PLANAR_U, info_nv->env); 136 | process_plane(info_nv->n, info_nv->src, info_nv->dst, info_nv->dstp_v, PLANAR_V, info_nv->env); 137 | } 138 | MemoryBarrier(); 139 | mt_info_reset_pointers(info); 140 | 141 | SetEvent(info->work_complete_event); 142 | WaitForSingleObject(info->work_event, INFINITE); 143 | } 144 | } 145 | 146 | static unsigned int __stdcall mt_proc_wrapper(void* filter_instance) 147 | { 148 | assert(filter_instance); 149 | ((f3kdb_avisynth*)filter_instance)->mt_proc(); 150 | return 0; 151 | } 152 | 153 | PVideoFrame __stdcall f3kdb_avisynth::GetFrame(int n, IScriptEnvironment* env) 154 | { 155 | PVideoFrame src = child->GetFrame(n, env); 156 | // interleaved 16bit output needs extra alignment 157 | PVideoFrame dst = env->NewVideoFrame(vi, PLANE_ALIGNMENT * 2); 158 | 159 | if (vi.IsPlanar() && !vi.IsY8()) 160 | { 161 | if (!_mt) 162 | { 163 | process_plane(n, src, dst, dst->GetWritePtr(PLANAR_Y), PLANAR_Y, env); 164 | process_plane(n, src, dst, dst->GetWritePtr(PLANAR_U), PLANAR_U, env); 165 | process_plane(n, src, dst, dst->GetWritePtr(PLANAR_V), PLANAR_V, env); 166 | } else { 167 | bool new_thread = _mt_info == NULL; 168 | if (new_thread) 169 | { 170 | _mt_info = mt_info_create(); 171 | if (!_mt_info) { 172 | env->ThrowError("f3kdb_avisynth: Failed to allocate mt_info."); 173 | return NULL; 174 | } 175 | } 176 | // we must get write pointer before copying the frame pointer 177 | // otherwise NULL will be returned 178 | unsigned char* dstp_y = dst->GetWritePtr(PLANAR_Y); 179 | _mt_info->n = n; 180 | _mt_info->dstp_u = dst->GetWritePtr(PLANAR_U); 181 | _mt_info->dstp_v = dst->GetWritePtr(PLANAR_V); 182 | _mt_info->env = env; 183 | MemoryBarrier(); 184 | { 185 | auto info_nv = const_cast(_mt_info); 186 | info_nv->dst = dst; 187 | info_nv->src = src; 188 | } 189 | MemoryBarrier(); 190 | if (!new_thread) 191 | { 192 | SetEvent(_mt_info->work_event); 193 | } 194 | else 195 | { 196 | _mt_info->thread_handle = (HANDLE)_beginthreadex(NULL, 0, mt_proc_wrapper, this, 0, NULL); 197 | if (!_mt_info->thread_handle) { 198 | int err = errno; 199 | mt_info_destroy(_mt_info); 200 | _mt_info = NULL; 201 | env->ThrowError("f3kdb_avisynth: Failed to create worker thread, code = %d.", err); 202 | return NULL; 203 | } 204 | } 205 | process_plane(n, src, dst, dstp_y, PLANAR_Y, env); 206 | WaitForSingleObject(_mt_info->work_complete_event, INFINITE); 207 | } 208 | } else { 209 | // Y8 210 | process_plane(n, src, dst, dst->GetWritePtr(), PLANAR_Y, env); 211 | } 212 | return dst; 213 | } -------------------------------------------------------------------------------- /src/avisynth/flash3kyuu_deband.def.h: -------------------------------------------------------------------------------- 1 | 2 | /************************* 3 | * Script generated code * 4 | * Do not modify * 5 | *************************/ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "avisynth.h" 12 | #include 13 | 14 | static const char* F3KDB_AVS_PARAMS = "c[range]i[Y]i[Cb]i[Cr]i[grainY]i[grainC]i[sample_mode]i[seed]i[blur_first]b[dynamic_grain]b[opt]i[mt]b[dither_algo]i[keep_tv_range]b[output_depth]i[random_algo_ref]i[random_algo_grain]i[random_param_ref]f[random_param_grain]f[preset]s"; 15 | 16 | typedef struct _F3KDB_RAW_ARGS 17 | { 18 | AVSValue child, range, Y, Cb, Cr, grainY, grainC, sample_mode, seed, blur_first, dynamic_grain, opt, mt, dither_algo, keep_tv_range, output_depth, random_algo_ref, random_algo_grain, random_param_ref, random_param_grain, preset; 19 | } F3KDB_RAW_ARGS; 20 | 21 | #define F3KDB_ARG_INDEX(name) (offsetof(F3KDB_RAW_ARGS, name) / sizeof(AVSValue)) 22 | 23 | #define F3KDB_ARG(name) args[F3KDB_ARG_INDEX(name)] 24 | 25 | #ifdef F3KDB_SIMPLE_MACRO_NAME 26 | 27 | #ifdef SIMPLE_MACRO_NAME 28 | #error Simple macro name has already been defined for SIMPLE_MACRO_NAME 29 | #endif 30 | 31 | #define SIMPLE_MACRO_NAME f3kdb 32 | 33 | #define ARG F3KDB_ARG 34 | 35 | #endif 36 | 37 | static void f3kdb_params_from_avs(AVSValue args, f3kdb_params_t* f3kdb_params) 38 | { 39 | if (F3KDB_ARG(range).Defined()) { f3kdb_params->range = F3KDB_ARG(range).AsInt(); } 40 | if (F3KDB_ARG(Y).Defined()) { f3kdb_params->Y = (unsigned short)F3KDB_ARG(Y).AsInt(); } 41 | if (F3KDB_ARG(Cb).Defined()) { f3kdb_params->Cb = (unsigned short)F3KDB_ARG(Cb).AsInt(); } 42 | if (F3KDB_ARG(Cr).Defined()) { f3kdb_params->Cr = (unsigned short)F3KDB_ARG(Cr).AsInt(); } 43 | if (F3KDB_ARG(grainY).Defined()) { f3kdb_params->grainY = F3KDB_ARG(grainY).AsInt(); } 44 | if (F3KDB_ARG(grainC).Defined()) { f3kdb_params->grainC = F3KDB_ARG(grainC).AsInt(); } 45 | if (F3KDB_ARG(sample_mode).Defined()) { f3kdb_params->sample_mode = F3KDB_ARG(sample_mode).AsInt(); } 46 | if (F3KDB_ARG(seed).Defined()) { f3kdb_params->seed = F3KDB_ARG(seed).AsInt(); } 47 | if (F3KDB_ARG(blur_first).Defined()) { f3kdb_params->blur_first = F3KDB_ARG(blur_first).AsBool(); } 48 | if (F3KDB_ARG(dynamic_grain).Defined()) { f3kdb_params->dynamic_grain = F3KDB_ARG(dynamic_grain).AsBool(); } 49 | if (F3KDB_ARG(opt).Defined()) { f3kdb_params->opt = (OPTIMIZATION_MODE)F3KDB_ARG(opt).AsInt(); } 50 | if (F3KDB_ARG(dither_algo).Defined()) { f3kdb_params->dither_algo = (DITHER_ALGORITHM)F3KDB_ARG(dither_algo).AsInt(); } 51 | if (F3KDB_ARG(keep_tv_range).Defined()) { f3kdb_params->keep_tv_range = F3KDB_ARG(keep_tv_range).AsBool(); } 52 | if (F3KDB_ARG(output_depth).Defined()) { f3kdb_params->output_depth = F3KDB_ARG(output_depth).AsInt(); } 53 | if (F3KDB_ARG(random_algo_ref).Defined()) { f3kdb_params->random_algo_ref = (RANDOM_ALGORITHM)F3KDB_ARG(random_algo_ref).AsInt(); } 54 | if (F3KDB_ARG(random_algo_grain).Defined()) { f3kdb_params->random_algo_grain = (RANDOM_ALGORITHM)F3KDB_ARG(random_algo_grain).AsInt(); } 55 | if (F3KDB_ARG(random_param_ref).Defined()) { f3kdb_params->random_param_ref = F3KDB_ARG(random_param_ref).AsFloat(); } 56 | if (F3KDB_ARG(random_param_grain).Defined()) { f3kdb_params->random_param_grain = F3KDB_ARG(random_param_grain).AsFloat(); } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/avisynth/mt_info.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "avisynth.h" 6 | 7 | #include 8 | #include 9 | 10 | typedef struct _mt_info 11 | { 12 | int n; 13 | PVideoFrame src; 14 | PVideoFrame dst; 15 | unsigned char *dstp_u; 16 | unsigned char *dstp_v; 17 | IScriptEnvironment* env; 18 | 19 | bool exit; 20 | 21 | HANDLE thread_handle; 22 | HANDLE work_event; 23 | HANDLE work_complete_event; 24 | } mt_info; 25 | 26 | static mt_info* mt_info_create(void) { 27 | mt_info* ret = (mt_info*)malloc(sizeof(mt_info)); 28 | if (ret) { 29 | memset(ret, 0, sizeof(mt_info)); 30 | ret->work_complete_event = CreateEvent(NULL, FALSE, FALSE, NULL); 31 | ret->work_event = CreateEvent(NULL, FALSE, FALSE, NULL); 32 | if (!ret->work_complete_event || !ret->work_event) { 33 | CloseHandle(ret->work_complete_event); 34 | CloseHandle(ret->work_event); 35 | free(ret); 36 | ret = NULL; 37 | } 38 | } 39 | return ret; 40 | } 41 | 42 | static void mt_info_reset_pointers(volatile mt_info* info) { 43 | assert(info); 44 | 45 | auto info_nv = const_cast(info); 46 | info_nv->dstp_u = NULL; 47 | info_nv->dstp_v = NULL; 48 | info_nv->src = NULL; 49 | info_nv->dst = NULL; 50 | MemoryBarrier(); 51 | } 52 | 53 | static void mt_info_destroy(volatile mt_info* info) { 54 | if (!info) { 55 | return; 56 | } 57 | 58 | assert(info->work_complete_event); 59 | assert(info->work_event); 60 | 61 | mt_info_reset_pointers(info); 62 | 63 | CloseHandle(info->work_complete_event); 64 | CloseHandle(info->work_event); 65 | CloseHandle(info->thread_handle); 66 | 67 | free((void*)info); 68 | } -------------------------------------------------------------------------------- /src/avisynth/stdafx.h: -------------------------------------------------------------------------------- 1 | // Just a stub 2 | #include "../stdafx.h" -------------------------------------------------------------------------------- /src/compiler_compat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__GNUC__) || defined(__INTEL_COMPILER) 4 | #define LIKELY(x) __builtin_expect((x),1) 5 | #define UNLIKELY(x) __builtin_expect((x),0) 6 | #define EXPECT(x, val) __builtin_expect((x),val) 7 | #else 8 | #define LIKELY(x) (x) 9 | #define UNLIKELY(x) (x) 10 | #define EXPECT(x, val) (x) 11 | #endif 12 | 13 | #ifdef __INTEL_COMPILER 14 | #define __PRAGMA_NOUNROLL__ __pragma(nounroll) 15 | #else 16 | #define __PRAGMA_NOUNROLL__ 17 | #endif 18 | 19 | #if defined(__GNUC__) || defined(__clang__) 20 | #include 21 | #include 22 | 23 | #define stricmp strcasecmp 24 | #define strnicmp strncasecmp 25 | #define _stricmp strcasecmp 26 | #define _strnicmp strncasecmp 27 | #define _snprintf snprintf 28 | #endif 29 | 30 | #ifndef _WIN32 31 | #include 32 | #define __forceinline inline 33 | #ifndef __cdecl 34 | #define __cdecl 35 | #endif 36 | #define InterlockedCompareExchangePointer(a,b,c) __sync_val_compare_and_swap(a,c,b) 37 | 38 | static inline void* _aligned_malloc(size_t size, size_t alignment) 39 | { 40 | void *tmp; 41 | if (posix_memalign(&tmp, alignment, size)) 42 | { 43 | tmp = 0; 44 | } 45 | return tmp; 46 | } 47 | #define _aligned_free free 48 | #endif 49 | 50 | #ifndef HAVE_ALIGNAS 51 | #if defined(_MSC_VER) || defined(__INTEL_COMPILER) 52 | #define _ALLOW_KEYWORD_MACROS 53 | #define alignas(x) __declspec(align(x)) 54 | #else 55 | #error "I don't know how to align variables" 56 | #endif 57 | #endif 58 | 59 | #if !defined(HAVE_ALIGNAS) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) 60 | #define ALIGNED_ARRAY(type, decl, alignment) type alignas(alignment) decl 61 | #else 62 | #define ALIGNED_ARRAY(type, decl, alignment) alignas(alignment) type decl 63 | #endif 64 | -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // alignment for SSE operations 4 | #define FRAME_LUT_ALIGNMENT 16 5 | 6 | #define INTERNAL_BIT_DEPTH 16 7 | 8 | // these range values are defined in internal bit depth 9 | #define TV_RANGE_Y_MIN (16 << (INTERNAL_BIT_DEPTH - 8)) 10 | #define TV_RANGE_Y_MAX (235 << (INTERNAL_BIT_DEPTH - 8)) 11 | 12 | #define TV_RANGE_C_MIN TV_RANGE_Y_MIN 13 | #define TV_RANGE_C_MAX (240 << (INTERNAL_BIT_DEPTH - 8)) 14 | 15 | #define FULL_RANGE_Y_MIN 0 16 | #define FULL_RANGE_Y_MAX ((1 << INTERNAL_BIT_DEPTH) - 1) 17 | 18 | #define FULL_RANGE_C_MIN FULL_RANGE_Y_MIN 19 | #define FULL_RANGE_C_MAX FULL_RANGE_Y_MAX 20 | 21 | #define VALUE_8BIT(x) ( x >> ( INTERNAL_BIT_DEPTH - 8 ) ) 22 | 23 | -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "process_plane_context.h" 5 | #include "compiler_compat.h" 6 | 7 | typedef struct _pixel_dither_info { 8 | alignas(4) signed char ref1; 9 | signed char ref2; 10 | signed short change; 11 | } pixel_dither_info; 12 | 13 | static_assert(sizeof(pixel_dither_info) == 4, "Something wrong in pixel_dither_info"); 14 | 15 | typedef struct _process_plane_params 16 | { 17 | const unsigned char *src_plane_ptr; 18 | int src_pitch; 19 | 20 | unsigned char *dst_plane_ptr; 21 | int dst_pitch; 22 | 23 | int plane_width_in_pixels; 24 | int plane_height_in_pixels; 25 | 26 | PIXEL_MODE input_mode; 27 | int input_depth; 28 | PIXEL_MODE output_mode; 29 | int output_depth; 30 | 31 | unsigned short threshold; 32 | pixel_dither_info *info_ptr_base; 33 | int info_stride; 34 | 35 | short* grain_buffer; 36 | int grain_buffer_stride; 37 | 38 | int plane; 39 | 40 | unsigned char width_subsampling; 41 | unsigned char height_subsampling; 42 | 43 | int pixel_max; 44 | int pixel_min; 45 | 46 | // Helper functions 47 | inline int get_dst_width() const { 48 | return output_mode == HIGH_BIT_DEPTH_INTERLEAVED ? plane_width_in_pixels * 2 : plane_width_in_pixels; 49 | } 50 | inline int get_dst_height() const { 51 | return plane_height_in_pixels; 52 | } 53 | inline int get_src_width() const { 54 | return input_mode == HIGH_BIT_DEPTH_INTERLEAVED ? plane_width_in_pixels * 2 : plane_width_in_pixels; 55 | } 56 | inline int get_src_height() const { 57 | return plane_height_in_pixels; 58 | } 59 | } process_plane_params; 60 | 61 | typedef void (*process_plane_impl_t)(const process_plane_params& params, process_plane_context* context); 62 | 63 | class f3kdb_core_t { 64 | private: 65 | process_plane_impl_t _process_plane_impl; 66 | 67 | pixel_dither_info *_y_info; 68 | pixel_dither_info *_cb_info; 69 | pixel_dither_info *_cr_info; 70 | 71 | process_plane_context _y_context; 72 | process_plane_context _cb_context; 73 | process_plane_context _cr_context; 74 | 75 | short* _grain_buffer_y; 76 | short* _grain_buffer_c; 77 | 78 | int* _grain_buffer_offsets; 79 | 80 | f3kdb_video_info_t _video_info; 81 | f3kdb_params_t _params; 82 | 83 | void init(void); 84 | void init_frame_luts(void); 85 | 86 | void destroy_frame_luts(void); 87 | 88 | f3kdb_core_t(const f3kdb_core_t&); 89 | f3kdb_core_t operator=(const f3kdb_core_t&); 90 | 91 | public: 92 | f3kdb_core_t(const f3kdb_video_info_t* video_info, const f3kdb_params_t* params); 93 | virtual ~f3kdb_core_t(); 94 | 95 | int process_plane(int frame_index, int plane, unsigned char* dst_frame_ptr, int dst_pitch, const unsigned char* src_frame_ptr, int src_pitch); 96 | }; -------------------------------------------------------------------------------- /src/debug_dump.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_dump.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "compiler_compat.h" 10 | 11 | #include 12 | 13 | #define DUMP_MAX_NAME_LENGTH 32 14 | #define DUMP_MAX_STAGES 32 15 | 16 | typedef struct _debug_dump_stage_t 17 | { 18 | FILE* fd; 19 | int items_in_current_line; 20 | TCHAR name[DUMP_MAX_NAME_LENGTH + 1]; 21 | } debug_dump_stage_t; 22 | 23 | typedef struct _debug_dump_t 24 | { 25 | TCHAR dump_path[MAX_PATH]; 26 | int items_per_line; 27 | debug_dump_stage_t stages[DUMP_MAX_STAGES]; 28 | } debug_dump_t; 29 | 30 | const static TCHAR* DUMP_BASE_PATH = TEXT("%TEMP%\\f3kdb_dump\\"); 31 | 32 | #define FAIL_ON_ZERO(expr) if (!(expr)) abort() 33 | 34 | alignas(4) 35 | static volatile DWORD _tls_slot = TLS_OUT_OF_INDEXES; 36 | 37 | void ensure_tls_slot() 38 | { 39 | DWORD tls_slot = _tls_slot; 40 | if (tls_slot == TLS_OUT_OF_INDEXES) 41 | { 42 | tls_slot = TlsAlloc(); 43 | if (InterlockedCompareExchange(&_tls_slot, tls_slot, TLS_OUT_OF_INDEXES) != TLS_OUT_OF_INDEXES) 44 | { 45 | // race failed, value initialized by other thread 46 | TlsFree(tls_slot); 47 | } 48 | assert(_tls_slot != TLS_OUT_OF_INDEXES); 49 | } 50 | 51 | } 52 | 53 | debug_dump_t* get_dump_handle(void) 54 | { 55 | ensure_tls_slot(); 56 | 57 | debug_dump_t* ret = (debug_dump_t*)TlsGetValue(_tls_slot); 58 | assert(GetLastError() == ERROR_SUCCESS); 59 | return ret; 60 | } 61 | 62 | debug_dump_t* create_dump_handle(void) 63 | { 64 | ensure_tls_slot(); 65 | 66 | debug_dump_t* ret = NULL; 67 | size_t size = sizeof(debug_dump_t); 68 | 69 | ret = (debug_dump_t*)malloc(size); 70 | memset(ret, 0, size); 71 | 72 | BOOL result = TlsSetValue(_tls_slot, ret); 73 | assert(result); 74 | 75 | return ret; 76 | } 77 | 78 | 79 | debug_dump_t* get_or_create_dump_handle(void) 80 | { 81 | debug_dump_t* ret = get_dump_handle(); 82 | 83 | if (!ret) 84 | { 85 | ret = create_dump_handle(); 86 | } 87 | 88 | return ret; 89 | } 90 | 91 | void dump_init(const TCHAR* dump_base_name, int plane, int items_per_line) 92 | { 93 | debug_dump_t* handle = get_or_create_dump_handle(); 94 | 95 | handle->items_per_line = items_per_line; 96 | 97 | assert(handle); 98 | assert(dump_base_name); 99 | 100 | 101 | FAIL_ON_ZERO(ExpandEnvironmentStrings(DUMP_BASE_PATH, handle->dump_path, MAX_PATH)); 102 | 103 | _tcscat(handle->dump_path, dump_base_name); 104 | 105 | TCHAR plane_str[4]; 106 | memset(plane_str, 0, sizeof(plane_str)); 107 | _sntprintf(plane_str, ARRAYSIZE(plane_str) - 1, TEXT("%d"), plane); 108 | 109 | _tcscat(handle->dump_path, TEXT("_")); 110 | _tcscat(handle->dump_path, plane_str); 111 | _tcscat(handle->dump_path, TEXT("\\")); 112 | 113 | DWORD attributes = GetFileAttributes(handle->dump_path); 114 | 115 | if (attributes == INVALID_FILE_ATTRIBUTES) 116 | { 117 | int ret = SHCreateDirectoryEx(NULL, handle->dump_path, NULL); 118 | if (ret != ERROR_SUCCESS) 119 | { 120 | abort(); 121 | } 122 | } else { 123 | if ( (attributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ) 124 | { 125 | abort(); 126 | } 127 | } 128 | } 129 | 130 | void dump_finish(void) 131 | { 132 | debug_dump_t* handle = get_dump_handle(); 133 | assert(handle); 134 | 135 | for (int i = 0; i < DUMP_MAX_STAGES; i++) 136 | { 137 | if (handle->stages[i].fd) 138 | { 139 | fclose(handle->stages[i].fd); 140 | memset(&(handle->stages[i]), 0, sizeof(debug_dump_stage_t)); 141 | } 142 | } 143 | 144 | free(handle); 145 | TlsSetValue(_tls_slot, NULL); 146 | } 147 | 148 | void dump_next_line() 149 | { 150 | debug_dump_t* handle = get_dump_handle(); 151 | assert(handle); 152 | 153 | const int dummy_value = 0xdeadbeef; 154 | 155 | for (int i = 0; i < DUMP_MAX_STAGES; i++) 156 | { 157 | if (!handle->stages[i].fd) 158 | { 159 | return; 160 | } 161 | while (handle->stages[i].items_in_current_line < handle->items_per_line) 162 | { 163 | fwrite(&dummy_value, 4, 1, handle->stages[i].fd); 164 | handle->stages[i].items_in_current_line++; 165 | } 166 | handle->stages[i].items_in_current_line = 0; 167 | } 168 | } 169 | 170 | static debug_dump_stage_t* find_or_create_dump_stage(const TCHAR* dump_name) 171 | { 172 | debug_dump_t* handle = get_dump_handle(); 173 | 174 | assert(handle); 175 | assert(dump_name); 176 | assert(handle->dump_path[0] != 0); 177 | assert(_tcslen(dump_name) <= DUMP_MAX_NAME_LENGTH); 178 | 179 | for (int i = 0; i < DUMP_MAX_STAGES; i++) 180 | { 181 | if (!_tcscmp(dump_name, handle->stages[i].name)) 182 | { 183 | return &handle->stages[i]; 184 | } 185 | 186 | if (!handle->stages[i].fd) 187 | { 188 | // fd not found, create one 189 | TCHAR file_name[MAX_PATH]; 190 | _tcscpy(file_name, handle->dump_path); 191 | _tcscat(file_name, dump_name); 192 | 193 | handle->stages[i].fd = _tfopen(file_name, TEXT("wb")); 194 | if (!handle->stages[i].fd) 195 | { 196 | abort(); 197 | } 198 | _tcscpy(handle->stages[i].name, dump_name); 199 | return &handle->stages[i]; 200 | } 201 | } 202 | 203 | // too many stages 204 | abort(); 205 | return NULL; 206 | } 207 | 208 | void dump_value(const TCHAR* dump_name, int value) 209 | { 210 | debug_dump_stage_t* stage = find_or_create_dump_stage(dump_name); 211 | debug_dump_t* handle = get_dump_handle(); 212 | if (stage->items_in_current_line >= handle->items_per_line) 213 | { 214 | return; 215 | } 216 | fwrite(&value, 4, 1, stage->fd); 217 | stage->items_in_current_line++; 218 | } 219 | 220 | void dump_value(const TCHAR* dump_name, __m128i value, int word_size_in_bytes, bool is_signed) 221 | { 222 | assert(word_size_in_bytes == 1 || word_size_in_bytes == 2 || word_size_in_bytes == 4); 223 | 224 | debug_dump_stage_t* stage = find_or_create_dump_stage(dump_name); 225 | debug_dump_t* handle = get_dump_handle(); 226 | 227 | alignas(16) 228 | char buffer[16]; 229 | 230 | _mm_store_si128((__m128i*)buffer, value); 231 | 232 | int item; 233 | for (int i = 0; i < 16 / word_size_in_bytes; i++) 234 | { 235 | if (stage->items_in_current_line >= handle->items_per_line) 236 | { 237 | return; 238 | } 239 | item = 0; 240 | memcpy(&item, buffer + i * word_size_in_bytes, word_size_in_bytes); 241 | if (is_signed) 242 | { 243 | int bits = (4 - word_size_in_bytes) * 8; 244 | item <<= bits; 245 | item >>= bits; 246 | } 247 | fwrite(&item, 4, 1, stage->fd); 248 | stage->items_in_current_line++; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/debug_dump.h: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | 5 | #ifdef ENABLE_DEBUG_DUMP 6 | 7 | #include 8 | 9 | void dump_init(const TCHAR* dump_base_name, int plane, int items_per_line); 10 | 11 | void dump_next_line(); 12 | 13 | void dump_value(const TCHAR* dump_name, int value); 14 | 15 | void dump_value(const TCHAR* dump_name, __m128i value, int word_size_in_bytes, bool is_signed); 16 | 17 | void dump_finish(); 18 | 19 | #define DUMP_INIT(name, plane, items_per_line) dump_init( TEXT(name), plane, items_per_line ) 20 | 21 | #define DUMP_NEXT_LINE() dump_next_line() 22 | 23 | #define DUMP_VALUE(name, ...) dump_value(TEXT(name), __VA_ARGS__) 24 | 25 | #define DUMP_VALUE_S(name, ...) dump_value(name, __VA_ARGS__) 26 | 27 | #define DUMP_FINISH() dump_finish() 28 | 29 | #else 30 | 31 | #define DUMP_INIT(name, ...) ((void)0) 32 | 33 | #define DUMP_NEXT_LINE() ((void)0) 34 | 35 | #define DUMP_VALUE(name, ...) ((void)0) 36 | 37 | #define DUMP_VALUE_S(name, ...) ((void)0) 38 | 39 | #define DUMP_FINISH() ((void)0) 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/dither_high.h: -------------------------------------------------------------------------------- 1 | 2 | #include "impl_dispatch.h" 3 | #include "compiler_compat.h" 4 | 5 | #define FS_DITHER_SKIP_PRE_CLAMP 6 | 7 | #include "pixel_proc_c_high_f_s_dithering.h" 8 | #include "pixel_proc_c_high_ordered_dithering.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace dither_high 14 | { 15 | static __m128i _ordered_dithering_threshold_map[16] [2]; 16 | static __m128i _ordered_dithering_threshold_map_yuy2[16] [8]; 17 | static volatile bool _threshold_map_initialized = false; 18 | 19 | static __inline void init_ordered_dithering() 20 | { 21 | if (UNLIKELY(!_threshold_map_initialized)) { 22 | __m128i threhold_row; 23 | __m128i zero = _mm_setzero_si128(); 24 | for (int i = 0; i < 16; i++) 25 | { 26 | threhold_row = *(__m128i*)pixel_proc_high_ordered_dithering::THRESHOLD_MAP[i]; 27 | 28 | __m128i part_0 = _mm_unpacklo_epi8(threhold_row, zero); 29 | __m128i part_1 = _mm_unpackhi_epi8(threhold_row, zero); 30 | 31 | if (INTERNAL_BIT_DEPTH < 16) 32 | { 33 | part_0 = _mm_srli_epi16(part_0, 16 - INTERNAL_BIT_DEPTH); 34 | part_1 = _mm_srli_epi16(part_1, 16 - INTERNAL_BIT_DEPTH); 35 | } 36 | _ordered_dithering_threshold_map[i][0] = part_0; 37 | _ordered_dithering_threshold_map[i][1] = part_1; 38 | 39 | __m128i tmp = _mm_unpacklo_epi8(part_0, part_0); 40 | _ordered_dithering_threshold_map_yuy2[i][0] = _mm_unpacklo_epi16(part_0, tmp); 41 | _ordered_dithering_threshold_map_yuy2[i][1] = _mm_unpackhi_epi16(part_0, tmp); 42 | 43 | tmp = _mm_unpackhi_epi8(part_0, part_0); 44 | _ordered_dithering_threshold_map_yuy2[i][2] = _mm_unpacklo_epi16(part_1, tmp); 45 | _ordered_dithering_threshold_map_yuy2[i][3] = _mm_unpackhi_epi16(part_1, tmp); 46 | 47 | tmp = _mm_unpacklo_epi8(part_1, part_1); 48 | _ordered_dithering_threshold_map_yuy2[i][4] = _mm_unpacklo_epi16(part_0, tmp); 49 | _ordered_dithering_threshold_map_yuy2[i][5] = _mm_unpackhi_epi16(part_0, tmp); 50 | 51 | tmp = _mm_unpackhi_epi8(part_1, part_1); 52 | _ordered_dithering_threshold_map_yuy2[i][6] = _mm_unpacklo_epi16(part_1, tmp); 53 | _ordered_dithering_threshold_map_yuy2[i][7] = _mm_unpackhi_epi16(part_1, tmp); 54 | } 55 | _mm_mfence(); 56 | _threshold_map_initialized = true; 57 | } 58 | } 59 | 60 | static void init_ordered_dithering_with_output_depth(char context_buffer[CONTEXT_BUFFER_SIZE], int output_depth) 61 | { 62 | assert(_threshold_map_initialized); 63 | 64 | __m128i shift = _mm_set_epi32(0, 0, 0, output_depth - 8); 65 | 66 | for (int i = 0; i < 16; i++) 67 | { 68 | for (int j = 0; j < 2; j++) 69 | { 70 | __m128i item = _ordered_dithering_threshold_map[i][j]; 71 | item = _mm_srl_epi16(item, shift); 72 | _mm_store_si128((__m128i*)(context_buffer + (i * 2 + j) * 16), item); 73 | } 74 | } 75 | } 76 | 77 | template 78 | static __inline void init(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 79 | { 80 | if (dither_algo == DA_HIGH_FLOYD_STEINBERG_DITHERING) 81 | { 82 | pixel_proc_high_f_s_dithering::init_context(context_buffer, frame_width, output_depth); 83 | } else if (dither_algo == DA_HIGH_ORDERED_DITHERING) { 84 | init_ordered_dithering(); 85 | init_ordered_dithering_with_output_depth(context_buffer, output_depth); 86 | } 87 | } 88 | 89 | template 90 | static __inline void complete(void* context) 91 | { 92 | if (dither_algo == DA_HIGH_FLOYD_STEINBERG_DITHERING) 93 | { 94 | pixel_proc_high_f_s_dithering::destroy_context(context); 95 | } 96 | } 97 | 98 | template 99 | static __forceinline __m128i dither(void* context, __m128i pixels, int row, int column) 100 | { 101 | switch (dither_algo) 102 | { 103 | case DA_HIGH_NO_DITHERING: 104 | return pixels; 105 | case DA_HIGH_ORDERED_DITHERING: 106 | { 107 | // row: use lowest 4 bits as index, mask = 0b00001111 = 15 108 | // column: always multiples of 8, so use 8 (bit 4) as selector, mask = 0b00001000 109 | assert((column & 7) == 0); 110 | __m128i threshold = _mm_load_si128((__m128i*)((char*)context + ( ( (row & 15) * 2 ) + ( (column & 8) >> 3 ) ) * 16 ) ); 111 | return _mm_adds_epu16(pixels, threshold); 112 | } 113 | case DA_HIGH_FLOYD_STEINBERG_DITHERING: 114 | // due to an ICC bug, accessing pixels using union will give us incorrect results 115 | // so we have to use a buffer here 116 | // tested on ICC 12.0.1024.2010 117 | alignas(16) 118 | unsigned short buffer[8]; 119 | _mm_store_si128((__m128i*)buffer, pixels); 120 | __PRAGMA_NOUNROLL__ 121 | for (int i = 0; i < 8; i++) 122 | { 123 | buffer[i] = (unsigned short)pixel_proc_high_f_s_dithering::dither(context, buffer[i], row, column + i); 124 | pixel_proc_high_f_s_dithering::next_pixel(context); 125 | } 126 | return _mm_load_si128((__m128i*)buffer); 127 | case DA_16BIT_INTERLEAVED: 128 | return _mm_setzero_si128(); 129 | break; 130 | default: 131 | abort(); 132 | return _mm_setzero_si128(); 133 | } 134 | } 135 | 136 | template 137 | static __forceinline __m128i dither_yuy2(char contexts[3][CONTEXT_BUFFER_SIZE], __m128i pixels, int row, int column) 138 | { 139 | switch (dither_algo) 140 | { 141 | case DA_HIGH_NO_DITHERING: 142 | return pixels; 143 | case DA_HIGH_ORDERED_DITHERING: 144 | // row: use lowest 4 bits as index, mask = 0b00001111 = 15 145 | // column: always multiples of 8, yuy2 threshold map has 8 items, mask = 0b00111000 146 | assert((column & 7) == 0); 147 | return _mm_adds_epu16(pixels, _ordered_dithering_threshold_map_yuy2[row & 15][(column >> 3) & 7]); 148 | case DA_HIGH_FLOYD_STEINBERG_DITHERING: 149 | // due to an ICC bug, accessing pixels using union will give us incorrect results 150 | // so we have to use a buffer here 151 | // tested on ICC 12.0.1024.2010 152 | alignas(16) 153 | unsigned short buffer[8]; 154 | _mm_store_si128((__m128i*)buffer, pixels); 155 | for (int i = 0; i < 8; i++) 156 | { 157 | int cur_column = column + i; 158 | void *cur_context; 159 | switch (i & 3) 160 | { 161 | case 0: 162 | case 2: 163 | cur_column >>= 1; 164 | cur_context = contexts[0]; 165 | break; 166 | case 1: 167 | cur_column >>= 2; 168 | cur_context = contexts[1]; 169 | break; 170 | case 3: 171 | cur_column >>= 2; 172 | cur_context = contexts[2]; 173 | break; 174 | } 175 | buffer[i] = (unsigned short)pixel_proc_high_f_s_dithering::dither(cur_context, buffer[i], row, cur_column); 176 | pixel_proc_high_f_s_dithering::next_pixel(cur_context); 177 | } 178 | return _mm_load_si128((__m128i*)buffer); 179 | case DA_16BIT_INTERLEAVED: 180 | return _mm_setzero_si128(); 181 | break; 182 | default: 183 | abort(); 184 | return _mm_setzero_si128(); 185 | } 186 | } 187 | 188 | template 189 | static __inline void next_row(void* context) 190 | { 191 | if (dither_algo == DA_HIGH_FLOYD_STEINBERG_DITHERING) 192 | { 193 | pixel_proc_high_f_s_dithering::next_row(context); 194 | } 195 | } 196 | }; -------------------------------------------------------------------------------- /src/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | 4 | BOOL APIENTRY DllMain( HMODULE hModule, 5 | DWORD ul_reason_for_call, 6 | LPVOID lpReserved 7 | ) 8 | { 9 | switch (ul_reason_for_call) 10 | { 11 | case DLL_PROCESS_ATTACH: 12 | case DLL_THREAD_ATTACH: 13 | case DLL_THREAD_DETACH: 14 | case DLL_PROCESS_DETACH: 15 | break; 16 | } 17 | return TRUE; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/flash3kyuu_deband_impl_c.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | // we intend the C version to be usable on most CPUs 4 | #define NO_SSE 5 | 6 | #include 7 | #include "core.h" 8 | #include "pixel_proc_c.h" 9 | #include 10 | #include "debug_dump.h" 11 | 12 | static inline bool _is_above_threshold(int threshold, int diff) { 13 | return abs(diff) >= threshold; 14 | } 15 | 16 | static inline bool is_above_threshold(int threshold, int diff1) { 17 | return _is_above_threshold(threshold, diff1); 18 | } 19 | 20 | static inline bool is_above_threshold(int threshold, int diff1, int diff2) { 21 | return _is_above_threshold(threshold, diff1) || 22 | _is_above_threshold(threshold, diff2); 23 | } 24 | 25 | static inline bool is_above_threshold(int threshold, int diff1, int diff2, int diff3, int diff4) { 26 | return _is_above_threshold(threshold, diff1) || 27 | _is_above_threshold(threshold, diff2) || 28 | _is_above_threshold(threshold, diff3) || 29 | _is_above_threshold(threshold, diff4); 30 | } 31 | 32 | template 33 | static __inline int read_pixel(const process_plane_params& params, void* context, const unsigned char* base, int offset = 0) 34 | { 35 | const unsigned char* ptr = base + offset; 36 | if (params.input_mode == LOW_BIT_DEPTH) 37 | { 38 | return pixel_proc_upsample(context, *ptr); 39 | } 40 | 41 | int ret; 42 | 43 | ret = *(unsigned short*)ptr; 44 | ret <<= (INTERNAL_BIT_DEPTH - params.input_depth); 45 | return ret; 46 | } 47 | 48 | template 49 | static __forceinline void __cdecl process_plane_plainc_mode12_high(const process_plane_params& params, process_plane_context*) 50 | { 51 | pixel_dither_info* info_ptr; 52 | char context[CONTEXT_BUFFER_SIZE]; 53 | 54 | unsigned short threshold = params.threshold; 55 | 56 | int pixel_min = params.pixel_min; 57 | int pixel_max = params.pixel_max; 58 | 59 | int width_subsamp = params.width_subsampling; 60 | 61 | pixel_proc_init_context(context, params.plane_width_in_pixels, params.output_depth); 62 | 63 | int pixel_step = params.input_mode == HIGH_BIT_DEPTH_INTERLEAVED ? 2 : 1; 64 | 65 | int process_width = params.plane_width_in_pixels; 66 | 67 | DUMP_INIT("c", params.plane, process_width); 68 | 69 | 70 | 71 | for (int i = 0; i < params.plane_height_in_pixels; i++) 72 | { 73 | const unsigned char* src_px = params.src_plane_ptr + params.src_pitch * i; 74 | unsigned char* dst_px = params.dst_plane_ptr + params.dst_pitch * i; 75 | 76 | const short* grain_buffer_ptr = params.grain_buffer + params.grain_buffer_stride * i; 77 | 78 | info_ptr = params.info_ptr_base + params.info_stride * i; 79 | 80 | 81 | for (int j = 0; j < process_width; j++) 82 | { 83 | pixel_dither_info info = *info_ptr; 84 | int src_px_up = read_pixel(params, context, src_px); 85 | 86 | DUMP_VALUE("src_px_up", src_px_up); 87 | 88 | assert(info.ref1 >= 0); 89 | assert((info.ref1 >> params.height_subsampling) <= i && 90 | (info.ref1 >> params.height_subsampling) + i < params.plane_height_in_pixels); 91 | 92 | 93 | DUMP_VALUE("ref1", info.ref1); 94 | 95 | int avg; 96 | bool use_org_px_as_base; 97 | int ref_pos, ref_pos_2; 98 | if (sample_mode == 1) 99 | { 100 | ref_pos = (info.ref1 >> params.height_subsampling) * params.src_pitch; 101 | 102 | DUMP_VALUE("ref_pos", ref_pos); 103 | 104 | int ref_1_up = read_pixel(params, context, src_px, ref_pos); 105 | int ref_2_up = read_pixel(params, context, src_px, -ref_pos); 106 | 107 | DUMP_VALUE("ref_1_up", ref_1_up); 108 | DUMP_VALUE("ref_2_up", ref_2_up); 109 | 110 | avg = pixel_proc_avg_2(context, ref_1_up, ref_2_up); 111 | 112 | if (blur_first) 113 | { 114 | int diff = avg - src_px_up; 115 | use_org_px_as_base = is_above_threshold(threshold, diff); 116 | } else { 117 | int diff = src_px_up - ref_1_up; 118 | int diff_n = src_px_up - ref_2_up; 119 | use_org_px_as_base = is_above_threshold(threshold, diff, diff_n); 120 | } 121 | } else { 122 | int x_multiplier = 1; 123 | 124 | assert(info.ref2 >= 0); 125 | assert((info.ref2 >> params.height_subsampling) <= i && 126 | (info.ref2 >> params.height_subsampling) + i < params.plane_height_in_pixels); 127 | assert(((info.ref1 >> width_subsamp) * x_multiplier) <= j && 128 | ((info.ref1 >> width_subsamp) * x_multiplier) + j < process_width); 129 | assert(((info.ref2 >> width_subsamp) * x_multiplier) <= j && 130 | ((info.ref2 >> width_subsamp) * x_multiplier) + j < process_width); 131 | 132 | 133 | DUMP_VALUE("ref2", info.ref2); 134 | 135 | ref_pos = params.src_pitch * (info.ref2 >> params.height_subsampling) + 136 | ((info.ref1 * x_multiplier) >> width_subsamp) * pixel_step; 137 | 138 | ref_pos_2 = ((info.ref2 * x_multiplier) >> width_subsamp) * pixel_step - 139 | params.src_pitch * (info.ref1 >> params.height_subsampling); 140 | 141 | 142 | DUMP_VALUE("ref_pos", ref_pos); 143 | DUMP_VALUE("ref_pos_2", ref_pos_2); 144 | 145 | int ref_1_up = read_pixel(params, context, src_px, ref_pos); 146 | int ref_2_up = read_pixel(params, context, src_px, ref_pos_2); 147 | int ref_3_up = read_pixel(params, context, src_px, -ref_pos); 148 | int ref_4_up = read_pixel(params, context, src_px, -ref_pos_2); 149 | 150 | 151 | DUMP_VALUE("ref_1_up", ref_1_up); 152 | DUMP_VALUE("ref_2_up", ref_2_up); 153 | DUMP_VALUE("ref_3_up", ref_3_up); 154 | DUMP_VALUE("ref_4_up", ref_4_up); 155 | 156 | avg = pixel_proc_avg_4(context, ref_1_up, ref_2_up, ref_3_up, ref_4_up); 157 | 158 | if (blur_first) 159 | { 160 | int diff = avg - src_px_up; 161 | use_org_px_as_base = is_above_threshold(threshold, diff); 162 | } else { 163 | int diff1 = ref_1_up - src_px_up; 164 | int diff2 = ref_2_up - src_px_up; 165 | int diff3 = ref_3_up - src_px_up; 166 | int diff4 = ref_4_up - src_px_up; 167 | use_org_px_as_base = is_above_threshold(threshold, diff1, diff2, diff3, diff4); 168 | } 169 | } 170 | 171 | DUMP_VALUE("avg", avg); 172 | DUMP_VALUE("change", *grain_buffer_ptr); 173 | 174 | int new_pixel; 175 | 176 | if (use_org_px_as_base) { 177 | new_pixel = src_px_up + *grain_buffer_ptr; 178 | } else { 179 | new_pixel = avg + *grain_buffer_ptr; 180 | } 181 | 182 | DUMP_VALUE("new_pixel_before_downsample", new_pixel); 183 | 184 | new_pixel = pixel_proc_downsample(context, new_pixel, i, j, pixel_min, pixel_max, params.output_depth); 185 | 186 | DUMP_VALUE("dst", new_pixel); 187 | switch (output_mode) 188 | { 189 | case LOW_BIT_DEPTH: 190 | *dst_px = (unsigned char)new_pixel; 191 | break; 192 | case HIGH_BIT_DEPTH_INTERLEAVED: 193 | *((unsigned short*)dst_px) = (unsigned short)(new_pixel & 0xFFFF); 194 | dst_px++; 195 | break; 196 | default: 197 | abort(); 198 | } 199 | 200 | src_px += pixel_step; 201 | dst_px++; 202 | info_ptr++; 203 | grain_buffer_ptr++; 204 | pixel_proc_next_pixel(context); 205 | } 206 | pixel_proc_next_row(context); 207 | DUMP_NEXT_LINE(); 208 | } 209 | 210 | DUMP_FINISH(); 211 | 212 | pixel_proc_destroy_context(context); 213 | } 214 | 215 | template 216 | void __cdecl process_plane_plainc(const process_plane_params& params, process_plane_context* context) 217 | { 218 | static_assert(sample_mode != 0, "No longer support sample_mode = 0"); 219 | switch (params.output_mode) 220 | { 221 | case LOW_BIT_DEPTH: 222 | process_plane_plainc_mode12_high(params, context); 223 | break; 224 | 225 | case HIGH_BIT_DEPTH_INTERLEAVED: 226 | process_plane_plainc_mode12_high(params, context); 227 | break; 228 | 229 | default: 230 | abort(); 231 | } 232 | } 233 | 234 | #define DECLARE_IMPL_C 235 | #include "impl_dispatch_decl.h" 236 | -------------------------------------------------------------------------------- /src/flash3kyuu_deband_impl_sse2.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | 5 | #define SSE_LIMIT 20 6 | 7 | #include "flash3kyuu_deband_sse_base.h" 8 | 9 | #define DECLARE_IMPL_SSE2 10 | #include "impl_dispatch_decl.h" 11 | -------------------------------------------------------------------------------- /src/flash3kyuu_deband_impl_sse4.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | 5 | #include "flash3kyuu_deband_sse_base.h" 6 | 7 | #define DECLARE_IMPL_SSE4 8 | #include "impl_dispatch_decl.h" 9 | -------------------------------------------------------------------------------- /src/flash3kyuu_deband_impl_ssse3.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | 5 | #define SSE_LIMIT 31 6 | 7 | #include "flash3kyuu_deband_sse_base.h" 8 | 9 | #define DECLARE_IMPL_SSSE3 10 | #include "impl_dispatch_decl.h" 11 | -------------------------------------------------------------------------------- /src/icc_override.cpp: -------------------------------------------------------------------------------- 1 | // Dispatcher-patch for Intel compiler 2 | // by Agner Fog (www.agner.org/optimize) 3 | 4 | #include "icc_override.h" 5 | 6 | #ifdef __INTEL_COMPILER 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { // Avoid C++ name mangling 12 | #endif 13 | 14 | // Global variable indicating cpu 15 | extern int __intel_cpu_indicator; 16 | 17 | enum { 18 | CPUID_FEAT_ECX_SSE3 = 1 << 0, 19 | CPUID_FEAT_ECX_PCLMUL = 1 << 1, 20 | CPUID_FEAT_ECX_DTES64 = 1 << 2, 21 | CPUID_FEAT_ECX_MONITOR = 1 << 3, 22 | CPUID_FEAT_ECX_DS_CPL = 1 << 4, 23 | CPUID_FEAT_ECX_VMX = 1 << 5, 24 | CPUID_FEAT_ECX_SMX = 1 << 6, 25 | CPUID_FEAT_ECX_EST = 1 << 7, 26 | CPUID_FEAT_ECX_TM2 = 1 << 8, 27 | CPUID_FEAT_ECX_SSSE3 = 1 << 9, 28 | CPUID_FEAT_ECX_CID = 1 << 10, 29 | CPUID_FEAT_ECX_FMA = 1 << 12, 30 | CPUID_FEAT_ECX_CX16 = 1 << 13, 31 | CPUID_FEAT_ECX_ETPRD = 1 << 14, 32 | CPUID_FEAT_ECX_PDCM = 1 << 15, 33 | CPUID_FEAT_ECX_DCA = 1 << 18, 34 | CPUID_FEAT_ECX_SSE4_1 = 1 << 19, 35 | CPUID_FEAT_ECX_SSE4_2 = 1 << 20, 36 | CPUID_FEAT_ECX_x2APIC = 1 << 21, 37 | CPUID_FEAT_ECX_MOVBE = 1 << 22, 38 | CPUID_FEAT_ECX_POPCNT = 1 << 23, 39 | CPUID_FEAT_ECX_AES = 1 << 25, 40 | CPUID_FEAT_ECX_XSAVE = 1 << 26, 41 | CPUID_FEAT_ECX_OSXSAVE = 1 << 27, 42 | CPUID_FEAT_ECX_AVX = 1 << 28, 43 | 44 | CPUID_FEAT_EDX_FPU = 1 << 0, 45 | CPUID_FEAT_EDX_VME = 1 << 1, 46 | CPUID_FEAT_EDX_DE = 1 << 2, 47 | CPUID_FEAT_EDX_PSE = 1 << 3, 48 | CPUID_FEAT_EDX_TSC = 1 << 4, 49 | CPUID_FEAT_EDX_MSR = 1 << 5, 50 | CPUID_FEAT_EDX_PAE = 1 << 6, 51 | CPUID_FEAT_EDX_MCE = 1 << 7, 52 | CPUID_FEAT_EDX_CX8 = 1 << 8, 53 | CPUID_FEAT_EDX_APIC = 1 << 9, 54 | CPUID_FEAT_EDX_SEP = 1 << 11, 55 | CPUID_FEAT_EDX_MTRR = 1 << 12, 56 | CPUID_FEAT_EDX_PGE = 1 << 13, 57 | CPUID_FEAT_EDX_MCA = 1 << 14, 58 | CPUID_FEAT_EDX_CMOV = 1 << 15, 59 | CPUID_FEAT_EDX_PAT = 1 << 16, 60 | CPUID_FEAT_EDX_PSE36 = 1 << 17, 61 | CPUID_FEAT_EDX_PSN = 1 << 18, 62 | CPUID_FEAT_EDX_CLF = 1 << 19, 63 | CPUID_FEAT_EDX_DTES = 1 << 21, 64 | CPUID_FEAT_EDX_ACPI = 1 << 22, 65 | CPUID_FEAT_EDX_MMX = 1 << 23, 66 | CPUID_FEAT_EDX_FXSR = 1 << 24, 67 | CPUID_FEAT_EDX_SSE = 1 << 25, 68 | CPUID_FEAT_EDX_SSE2 = 1 << 26, 69 | CPUID_FEAT_EDX_SS = 1 << 27, 70 | CPUID_FEAT_EDX_HTT = 1 << 28, 71 | CPUID_FEAT_EDX_TM1 = 1 << 29, 72 | CPUID_FEAT_EDX_IA64 = 1 << 30, 73 | CPUID_FEAT_EDX_PBE = 1 << 31 74 | }; 75 | 76 | // CPU dispatcher function 77 | void ___intel_cpu_indicator_init() { 78 | int cpu_info[4] = {-1}; 79 | __cpuid(cpu_info, 1); 80 | int ecx = cpu_info[2]; 81 | int edx = cpu_info[3]; 82 | if (ecx & CPUID_FEAT_ECX_AVX) 83 | { 84 | __intel_cpu_indicator = 0x20000; 85 | } else if (ecx & CPUID_FEAT_ECX_SSE4_2) { 86 | __intel_cpu_indicator = 0x8000; 87 | } else if (ecx & CPUID_FEAT_ECX_SSE4_1) { 88 | __intel_cpu_indicator = 0x2000; 89 | } else if (ecx & CPUID_FEAT_ECX_SSSE3) { 90 | __intel_cpu_indicator = 0x1000; 91 | } else if (ecx & CPUID_FEAT_ECX_SSE3) { 92 | __intel_cpu_indicator = 0x800; 93 | } else if (edx & CPUID_FEAT_EDX_SSE2) { 94 | __intel_cpu_indicator = 0x200; 95 | } else if (edx & CPUID_FEAT_EDX_SSE2) { 96 | __intel_cpu_indicator = 0x80; 97 | } else if (edx & CPUID_FEAT_EDX_MMX) { 98 | __intel_cpu_indicator = 8; 99 | } else { 100 | __intel_cpu_indicator = 1; 101 | } 102 | } 103 | #ifdef __cplusplus 104 | } // End of extern "C" 105 | #endif 106 | 107 | #endif -------------------------------------------------------------------------------- /src/icc_override.h: -------------------------------------------------------------------------------- 1 | 2 | #ifdef __INTEL_COMPILER 3 | extern "C" void ___intel_cpu_indicator_init(); 4 | #else 5 | #define ___intel_cpu_indicator_init() 6 | #endif -------------------------------------------------------------------------------- /src/impl_dispatch.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "core.h" 4 | 5 | #define IMPL_DISPATCH_IMPORT_DECLARATION 6 | 7 | #include "impl_dispatch_decl.h" 8 | 9 | const process_plane_impl_t* process_plane_impl_high_precision_no_dithering[] = { 10 | process_plane_impl_c_high_no_dithering, 11 | process_plane_impl_sse2_high_no_dithering, 12 | process_plane_impl_ssse3_high_no_dithering, 13 | process_plane_impl_sse4_high_no_dithering 14 | }; 15 | 16 | const process_plane_impl_t* process_plane_impl_high_precision_ordered_dithering[] = { 17 | process_plane_impl_c_high_ordered_dithering, 18 | process_plane_impl_sse2_high_ordered_dithering, 19 | process_plane_impl_ssse3_high_ordered_dithering, 20 | process_plane_impl_sse4_high_ordered_dithering 21 | }; 22 | 23 | const process_plane_impl_t* process_plane_impl_high_precision_floyd_steinberg_dithering[] = { 24 | process_plane_impl_c_high_floyd_steinberg_dithering, 25 | process_plane_impl_sse2_high_floyd_steinberg_dithering, 26 | process_plane_impl_ssse3_high_floyd_steinberg_dithering, 27 | process_plane_impl_sse4_high_floyd_steinberg_dithering 28 | }; 29 | 30 | const process_plane_impl_t* process_plane_impl_16bit_interleaved[] = { 31 | process_plane_impl_c_16bit_interleaved, 32 | process_plane_impl_sse2_16bit_interleaved, 33 | process_plane_impl_ssse3_16bit_interleaved, 34 | process_plane_impl_sse4_16bit_interleaved 35 | }; 36 | 37 | 38 | const process_plane_impl_t** process_plane_impls[] = { 39 | nullptr, // process_plane_impl_low_precision has been removed, 40 | process_plane_impl_high_precision_no_dithering, 41 | process_plane_impl_high_precision_ordered_dithering, 42 | process_plane_impl_high_precision_floyd_steinberg_dithering, 43 | process_plane_impl_16bit_interleaved 44 | }; -------------------------------------------------------------------------------- /src/impl_dispatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core.h" 4 | 5 | extern const process_plane_impl_t** process_plane_impls[]; 6 | 7 | #define DITHER_CONTEXT_BUFFER_SIZE 8192 8 | 9 | #define CONTEXT_BUFFER_SIZE DITHER_CONTEXT_BUFFER_SIZE 10 | -------------------------------------------------------------------------------- /src/impl_dispatch_decl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core.h" 4 | 5 | #ifdef IMPL_DISPATCH_IMPORT_DECLARATION 6 | 7 | #define DEFINE_IMPL(n, \ 8 | nullptr, \ 9 | impl_func_mode1_blur, \ 10 | impl_func_mode1_noblur, \ 11 | impl_func_mode2_blur, \ 12 | impl_func_mode2_noblur) \ 13 | extern const process_plane_impl_t process_plane_impl_##n []; 14 | 15 | #else 16 | 17 | #define DEFINE_IMPL(n, \ 18 | nullptr, \ 19 | impl_func_mode1_blur, \ 20 | impl_func_mode1_noblur, \ 21 | impl_func_mode2_blur, \ 22 | impl_func_mode2_noblur) \ 23 | extern const process_plane_impl_t process_plane_impl_##n [] = { \ 24 | nullptr, \ 25 | impl_func_mode1_blur, \ 26 | impl_func_mode1_noblur, \ 27 | impl_func_mode2_blur, \ 28 | impl_func_mode2_noblur}; 29 | 30 | #endif 31 | 32 | 33 | #define DEFINE_TEMPLATE_IMPL(name, impl_func, ...) \ 34 | DEFINE_IMPL(name, \ 35 | (nullptr), \ 36 | (&impl_func<1, true, __VA_ARGS__>), \ 37 | (&impl_func<1, false, __VA_ARGS__>), \ 38 | (&impl_func<2, true, __VA_ARGS__>), \ 39 | (&impl_func<2, false, __VA_ARGS__>) ); 40 | 41 | #define DEFINE_SSE_IMPL(name, ...) \ 42 | DEFINE_TEMPLATE_IMPL(name, process_plane_sse_impl, __VA_ARGS__); 43 | 44 | 45 | #if defined(IMPL_DISPATCH_IMPORT_DECLARATION) || defined(DECLARE_IMPL_C) 46 | DEFINE_TEMPLATE_IMPL(c_high_no_dithering, process_plane_plainc, DA_HIGH_NO_DITHERING); 47 | DEFINE_TEMPLATE_IMPL(c_high_ordered_dithering, process_plane_plainc, DA_HIGH_ORDERED_DITHERING); 48 | DEFINE_TEMPLATE_IMPL(c_high_floyd_steinberg_dithering, process_plane_plainc, DA_HIGH_FLOYD_STEINBERG_DITHERING); 49 | DEFINE_TEMPLATE_IMPL(c_16bit_interleaved, process_plane_plainc, DA_16BIT_INTERLEAVED); 50 | #endif 51 | 52 | 53 | #if defined(IMPL_DISPATCH_IMPORT_DECLARATION) || defined(DECLARE_IMPL_SSE4) 54 | DEFINE_SSE_IMPL(sse4_high_no_dithering, DA_HIGH_NO_DITHERING); 55 | DEFINE_SSE_IMPL(sse4_high_ordered_dithering, DA_HIGH_ORDERED_DITHERING); 56 | DEFINE_SSE_IMPL(sse4_high_floyd_steinberg_dithering, DA_HIGH_FLOYD_STEINBERG_DITHERING); 57 | DEFINE_SSE_IMPL(sse4_16bit_interleaved, DA_16BIT_INTERLEAVED); 58 | #endif 59 | 60 | 61 | #if defined(IMPL_DISPATCH_IMPORT_DECLARATION) || defined(DECLARE_IMPL_SSSE3) 62 | DEFINE_SSE_IMPL(ssse3_high_no_dithering, DA_HIGH_NO_DITHERING); 63 | DEFINE_SSE_IMPL(ssse3_high_ordered_dithering, DA_HIGH_ORDERED_DITHERING); 64 | DEFINE_SSE_IMPL(ssse3_high_floyd_steinberg_dithering, DA_HIGH_FLOYD_STEINBERG_DITHERING); 65 | DEFINE_SSE_IMPL(ssse3_16bit_interleaved, DA_16BIT_INTERLEAVED); 66 | #endif 67 | 68 | 69 | #if defined(IMPL_DISPATCH_IMPORT_DECLARATION) || defined(DECLARE_IMPL_SSE2) 70 | DEFINE_SSE_IMPL(sse2_high_no_dithering, DA_HIGH_NO_DITHERING); 71 | DEFINE_SSE_IMPL(sse2_high_ordered_dithering, DA_HIGH_ORDERED_DITHERING); 72 | DEFINE_SSE_IMPL(sse2_high_floyd_steinberg_dithering, DA_HIGH_FLOYD_STEINBERG_DITHERING); 73 | DEFINE_SSE_IMPL(sse2_16bit_interleaved, DA_16BIT_INTERLEAVED); 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /src/pixel_proc_c.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "impl_dispatch.h" 6 | 7 | #define CALL_IMPL(func, ...) \ 8 | ( mode == DA_HIGH_NO_DITHERING ? pixel_proc_high_no_dithering::func(__VA_ARGS__) : \ 9 | mode == DA_HIGH_ORDERED_DITHERING ? pixel_proc_high_ordered_dithering::func(__VA_ARGS__) : \ 10 | mode == DA_HIGH_FLOYD_STEINBERG_DITHERING ? pixel_proc_high_f_s_dithering::func(__VA_ARGS__) : \ 11 | pixel_proc_16bit::func(__VA_ARGS__) ) 12 | 13 | #define CHECK_MODE() if (mode < 0 || mode >= DA_COUNT) abort() 14 | 15 | #include "pixel_proc_c_high_no_dithering.h" 16 | #include "pixel_proc_c_high_ordered_dithering.h" 17 | #include "pixel_proc_c_high_f_s_dithering.h" 18 | 19 | #include "pixel_proc_c_16bit.h" 20 | 21 | template 22 | static inline void pixel_proc_init_context(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 23 | { 24 | CHECK_MODE(); 25 | CALL_IMPL(init_context, context_buffer, frame_width, output_depth); 26 | } 27 | 28 | template 29 | static inline void pixel_proc_destroy_context(void* context) 30 | { 31 | CHECK_MODE(); 32 | CALL_IMPL(destroy_context, context); 33 | } 34 | 35 | template 36 | static inline void pixel_proc_next_pixel(void* context) 37 | { 38 | CHECK_MODE(); 39 | CALL_IMPL(next_pixel, context); 40 | } 41 | 42 | template 43 | static inline void pixel_proc_next_row(void* context) 44 | { 45 | CHECK_MODE(); 46 | CALL_IMPL(next_row, context); 47 | } 48 | 49 | template 50 | static inline int pixel_proc_upsample(void* context, unsigned char pixel) 51 | { 52 | CHECK_MODE(); 53 | return CALL_IMPL(upsample, context, pixel); 54 | } 55 | 56 | template 57 | static inline int pixel_proc_downsample(void* context, int pixel, int row, int column, int pixel_min, int pixel_max, int output_depth) 58 | { 59 | CHECK_MODE(); 60 | return CALL_IMPL(downsample, context, pixel, row, column, pixel_min, pixel_max, output_depth); 61 | } 62 | 63 | template 64 | static inline int pixel_proc_avg_2(void* context, int pixel1, int pixel2) 65 | { 66 | CHECK_MODE(); 67 | return CALL_IMPL(avg_2, context, pixel1, pixel2); 68 | } 69 | 70 | template 71 | static inline int pixel_proc_avg_4(void* context, int pixel1, int pixel2, int pixel3, int pixel4) 72 | { 73 | CHECK_MODE(); 74 | return CALL_IMPL(avg_4, context, pixel1, pixel2, pixel3, pixel4); 75 | } 76 | -------------------------------------------------------------------------------- /src/pixel_proc_c_16bit.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace pixel_proc_16bit { 4 | 5 | static inline void init_context(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 6 | { 7 | // sanity check only 8 | assert(output_depth == 16); 9 | } 10 | 11 | static inline void destroy_context(void* context) 12 | { 13 | // nothing to do 14 | } 15 | 16 | static inline void next_pixel(void* context) 17 | { 18 | // nothing to do 19 | } 20 | 21 | static inline void next_row(void* context) 22 | { 23 | // nothing to do 24 | } 25 | 26 | static inline int dither(void* context, int pixel, int row, int column) 27 | { 28 | return pixel; 29 | } 30 | 31 | #define HAS_DOWNSAMPLE 32 | 33 | #include "pixel_proc_c_high_bit_depth_common.h" 34 | 35 | static inline int downsample(void* context, int pixel, int row, int column, int pixel_min, int pixel_max, int output_depth) 36 | { 37 | assert(output_depth == 16); 38 | // I know the method name is totally wrong... 39 | return clamp_pixel(pixel, pixel_min, pixel_max) << (output_depth - INTERNAL_BIT_DEPTH); 40 | } 41 | 42 | 43 | }; -------------------------------------------------------------------------------- /src/pixel_proc_c_high_bit_depth_common.h: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include "constants.h" 3 | 4 | static inline int upsample(void* context, unsigned char pixel) 5 | { 6 | return pixel << (INTERNAL_BIT_DEPTH - 8); 7 | } 8 | 9 | #if defined(HAS_DOWNSAMPLE) 10 | #undef HAS_DOWNSAMPLE 11 | #else 12 | static inline int downsample(void* context, int pixel, int row, int column, int pixel_min, int pixel_max, int output_depth) 13 | { 14 | pixel = dither(context, pixel, row, column); 15 | return clamp_pixel(pixel, pixel_min, pixel_max) >> (INTERNAL_BIT_DEPTH - output_depth); 16 | } 17 | #endif 18 | 19 | static inline int avg_2(void* context, int pixel1, int pixel2) 20 | { 21 | return (pixel1 + pixel2 + 1) >> 1; 22 | } 23 | 24 | static inline int avg_4(void* context, int pixel1, int pixel2, int pixel3, int pixel4) 25 | { 26 | // consistent with SSE code 27 | int avg1 = (pixel1 + pixel2 + 1) >> 1; 28 | int avg2 = (pixel3 + pixel4 + 1) >> 1; 29 | if (avg1 > 0) 30 | { 31 | avg1 -= 1; 32 | } 33 | return (avg1 + avg2 + 1) >> 1; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/pixel_proc_c_high_f_s_dithering.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "impl_dispatch.h" 6 | 7 | namespace pixel_proc_high_f_s_dithering { 8 | 9 | // #define DUMP_DATA 10 | 11 | typedef unsigned short ERROR_TYPE; 12 | 13 | typedef struct _context_t 14 | { 15 | int output_depth; 16 | ERROR_TYPE* error_buffer; 17 | bool buffer_needs_dealloc; 18 | ERROR_TYPE* current_px_error; 19 | int row_pitch; 20 | int frame_width; 21 | int processed_pixels_in_current_line; 22 | #ifdef DUMP_DATA 23 | FILE* debug_dump_fd[3]; 24 | #endif 25 | } context_t; 26 | 27 | static inline void init_context(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 28 | { 29 | context_t* ctx = (context_t*)context_buffer; 30 | int ctx_size = sizeof(context_t); 31 | #if defined(NO_SSE) && defined(__INTEL_COMPILER) 32 | #pragma novector 33 | for (int i = 0; i < ctx_size; i++) 34 | { 35 | *( ( (unsigned char*)ctx ) + i ) = 0; 36 | } 37 | #else 38 | // ICC will generate SSE code here 39 | memset(ctx, 0, ctx_size); 40 | #endif 41 | 42 | // additional 2 bytes are placed at the beginning and the end 43 | int size_needed = (frame_width + 2) * 2 * sizeof(ERROR_TYPE); 44 | if (CONTEXT_BUFFER_SIZE - ctx_size < size_needed) 45 | { 46 | ctx->error_buffer = (ERROR_TYPE*)malloc(size_needed); 47 | ctx->buffer_needs_dealloc = true; 48 | } else { 49 | ctx->error_buffer = (ERROR_TYPE*)(context_buffer + ctx_size); 50 | } 51 | memset(ctx->error_buffer, 0, size_needed); 52 | ctx->current_px_error = ctx->error_buffer + 1; 53 | ctx->row_pitch = frame_width + 2; 54 | ctx->frame_width = frame_width; 55 | ctx->output_depth = output_depth; 56 | 57 | #ifdef DUMP_DATA 58 | char file_name[256]; 59 | sprintf(file_name, "fsdither_dump_stage0_%d", frame_width); 60 | ctx->debug_dump_fd[0] = fopen(file_name, "wb"); 61 | sprintf(file_name, "fsdither_dump_stage1_%d", frame_width); 62 | ctx->debug_dump_fd[1] = fopen(file_name, "wb"); 63 | sprintf(file_name, "fsdither_dump_stage2_%d", frame_width); 64 | ctx->debug_dump_fd[2] = fopen(file_name, "wb"); 65 | #endif 66 | } 67 | 68 | static inline void destroy_context(void* context) 69 | { 70 | context_t* ctx = (context_t*)context; 71 | if (ctx->buffer_needs_dealloc) 72 | { 73 | free(ctx->error_buffer); 74 | ctx->error_buffer = NULL; 75 | } 76 | #ifdef DUMP_DATA 77 | for (int i = 0; i < sizeof(ctx->debug_dump_fd) / sizeof(FILE*); i++) 78 | { 79 | if (ctx->debug_dump_fd[i]) 80 | { 81 | fclose(ctx->debug_dump_fd[i]); 82 | } 83 | } 84 | #endif 85 | } 86 | 87 | static __forceinline void next_pixel(void* context) 88 | { 89 | context_t* ctx = (context_t*)context; 90 | ctx->current_px_error++; 91 | ctx->processed_pixels_in_current_line++; 92 | } 93 | 94 | static __forceinline void next_row(void* context) 95 | { 96 | context_t* ctx = (context_t*)context; 97 | ctx->row_pitch = -ctx->row_pitch; 98 | ctx->current_px_error = ctx->error_buffer + (ctx->row_pitch >> 31) * ctx->row_pitch; 99 | memset(ctx->current_px_error + ctx->row_pitch, 0, abs(ctx->row_pitch) * sizeof(ERROR_TYPE)); 100 | ctx->current_px_error++; 101 | ctx->processed_pixels_in_current_line = 0; 102 | } 103 | 104 | static __forceinline int dither(void* context, int pixel, int row, int column); 105 | 106 | #include "pixel_proc_c_high_bit_depth_common.h" 107 | 108 | static const int PIXEL_MAX = ( ( 1 << (INTERNAL_BIT_DEPTH) ) - 1 ); 109 | static const int PIXEL_MIN = 0; 110 | 111 | static __forceinline int dither(void* context, int pixel, int row, int column) 112 | { 113 | context_t* ctx = (context_t*)context; 114 | if (ctx->processed_pixels_in_current_line >= ctx->frame_width) 115 | { 116 | // outside plane, can occur in SSE code 117 | return pixel; 118 | } 119 | #ifndef FS_DITHER_SKIP_PRE_CLAMP 120 | pixel = clamp_pixel(pixel, PIXEL_MIN, PIXEL_MAX); 121 | #endif 122 | #ifdef DUMP_DATA 123 | fwrite(&pixel, 4, 1, ctx->debug_dump_fd[0]); 124 | #endif 125 | pixel += *(ctx->current_px_error); 126 | #ifdef DUMP_DATA 127 | fwrite(&pixel, 4, 1, ctx->debug_dump_fd[1]); 128 | #endif 129 | pixel = clamp_pixel(pixel, PIXEL_MIN, PIXEL_MAX); 130 | #ifdef DUMP_DATA 131 | fwrite(&pixel, 4, 1, ctx->debug_dump_fd[2]); 132 | #endif 133 | int new_error = pixel & ( ( 1 << (INTERNAL_BIT_DEPTH - ctx->output_depth) ) - 1 ); 134 | *(ctx->current_px_error + 1) += (new_error * 7) >> 4; 135 | *(ctx->current_px_error + ctx->row_pitch - 1) += (new_error * 3) >> 4; 136 | *(ctx->current_px_error + ctx->row_pitch) += (new_error * 5) >> 4; 137 | *(ctx->current_px_error + ctx->row_pitch + 1) += (new_error * 1) >> 4; 138 | return pixel; 139 | } 140 | 141 | 142 | }; -------------------------------------------------------------------------------- /src/pixel_proc_c_high_no_dithering.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace pixel_proc_high_no_dithering { 4 | 5 | static inline void init_context(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 6 | { 7 | // nothing to do 8 | } 9 | 10 | static inline void destroy_context(void* context) 11 | { 12 | // nothing to do 13 | } 14 | 15 | static inline void next_pixel(void* context) 16 | { 17 | // nothing to do 18 | } 19 | 20 | static inline void next_row(void* context) 21 | { 22 | // nothing to do 23 | } 24 | 25 | static inline int dither(void* context, int pixel, int row, int column) 26 | { 27 | return pixel; 28 | } 29 | 30 | #include "pixel_proc_c_high_bit_depth_common.h" 31 | 32 | }; -------------------------------------------------------------------------------- /src/pixel_proc_c_high_ordered_dithering.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace pixel_proc_high_ordered_dithering { 4 | 5 | // bayer dither matrix 6 | // align to 16 byte for reading from SSE code 7 | ALIGNED_ARRAY(static const unsigned char, THRESHOLD_MAP [16] [16], 16) = 8 | { 9 | { 0, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170 }, 10 | { 192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106 }, 11 | { 48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154 }, 12 | { 240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90 }, 13 | { 12, 140, 44, 172, 4, 132, 36, 164, 14, 142, 46, 174, 6, 134, 38, 166 }, 14 | { 204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102 }, 15 | { 60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150 }, 16 | { 252, 124, 220, 92, 244, 116, 212, 84, 254, 126, 222, 94, 246, 118, 214, 86 }, 17 | { 3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169 }, 18 | { 195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105 }, 19 | { 51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153 }, 20 | { 243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89 }, 21 | { 15, 143, 47, 175, 7, 135, 39, 167, 13, 141, 45, 173, 5, 133, 37, 165 }, 22 | { 207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101 }, 23 | { 63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149 }, 24 | { 255, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85 } 25 | }; 26 | 27 | static const int THRESHOLD_MAP_RIGHT_SHIFT_BITS = 16 - INTERNAL_BIT_DEPTH; 28 | 29 | 30 | static inline void init_context(char context_buffer[CONTEXT_BUFFER_SIZE], int frame_width, int output_depth) 31 | { 32 | *((int*)context_buffer) = output_depth; 33 | } 34 | 35 | static inline void destroy_context(void* context) 36 | { 37 | // nothing to do 38 | } 39 | 40 | static inline void next_pixel(void* context) 41 | { 42 | // nothing to do 43 | } 44 | 45 | static inline void next_row(void* context) 46 | { 47 | // nothing to do 48 | } 49 | 50 | static inline int dither(void* context, int pixel, int row, int column) 51 | { 52 | int output_depth = *(int*)context; 53 | pixel += (THRESHOLD_MAP[row & 15][column & 15] >> (THRESHOLD_MAP_RIGHT_SHIFT_BITS + output_depth - 8)); 54 | return pixel; 55 | } 56 | 57 | #include "pixel_proc_c_high_bit_depth_common.h" 58 | }; 59 | -------------------------------------------------------------------------------- /src/presets.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static const char* const PRESETS[] = { 4 | "depth", 5 | "y=0/cb=0/cr=0/grainy=0/grainc=0", 6 | "low", 7 | "y=32/cb=32/cr=32/grainy=32/grainc=32", 8 | "medium", 9 | "y=48/cb=48/cr=48/grainy=48/grainc=48", 10 | "high", 11 | "y=64/cb=64/cr=64/grainy=64/grainc=64", 12 | "veryhigh", 13 | "y=80/cb=80/cr=80/grainy=80/grainc=80", 14 | "nograin", 15 | "grainy=0/grainc=0", 16 | "luma", 17 | "cb=0/cr=0/grainc=0", 18 | "chroma", 19 | "y=0/grainy=0", 20 | nullptr, nullptr 21 | }; 22 | 23 | static_assert((sizeof(PRESETS) / sizeof(PRESETS[0])) % 2 == 0, "Incorrect preset definition"); 24 | 25 | -------------------------------------------------------------------------------- /src/process_plane_context.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "process_plane_context.h" 4 | 5 | #include 6 | #include 7 | 8 | // disable SSE instructions here to allow running on pre-SSE2 systems 9 | #if defined(__INTEL_COMPILER) 10 | 11 | void safe_zero_memory(void* start_address, size_t count) 12 | { 13 | char* ptr = (char*)start_address; 14 | #pragma novector 15 | for (int i = 0; i < count; i++) 16 | { 17 | *(ptr + i) = 0; 18 | } 19 | } 20 | 21 | #else 22 | 23 | #define safe_zero_memory(start_address, count) memset(start_address, 0, count) 24 | 25 | #endif 26 | 27 | void destroy_context(process_plane_context* context) 28 | { 29 | assert(context); 30 | 31 | if (context->data) { 32 | assert(context->destroy); 33 | context->destroy(context->data); 34 | safe_zero_memory(context, sizeof(process_plane_context)); 35 | } 36 | } 37 | 38 | void init_context(process_plane_context* context) 39 | { 40 | assert(context); 41 | safe_zero_memory(context, sizeof(process_plane_context)); 42 | } 43 | -------------------------------------------------------------------------------- /src/process_plane_context.h: -------------------------------------------------------------------------------- 1 | 2 | typedef void (*destroy_data_t)(void* data); 3 | 4 | typedef struct _process_plane_context 5 | { 6 | void* data; 7 | destroy_data_t destroy; 8 | } process_plane_context; 9 | 10 | void destroy_context(process_plane_context* context); 11 | 12 | void init_context(process_plane_context* context); -------------------------------------------------------------------------------- /src/public_interface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "compiler_compat.h" 10 | #include "core.h" 11 | #include "auto_utils.h" 12 | #include "constants.h" 13 | #include "impl_dispatch.h" 14 | #include "presets.h" 15 | 16 | using namespace std; 17 | 18 | F3KDB_API(int) f3kdb_params_init_defaults(f3kdb_params_t* params, int interface_version) 19 | { 20 | if (interface_version != F3KDB_INTERFACE_VERSION) 21 | { 22 | return F3KDB_ERROR_INVALID_INTERFACE_VERSION; 23 | } 24 | memset(params, 0, sizeof(f3kdb_params_t)); 25 | params_set_defaults(params); 26 | return F3KDB_SUCCESS; 27 | } 28 | 29 | static int parse_param_string(const char* params, bool has_name, function item_callback) 30 | { 31 | string param_string(params); 32 | string name_str, value_str; 33 | size_t pos = 0; 34 | for (size_t i = 0; i < param_string.size() + 1; i++) 35 | { 36 | switch (param_string[i]) 37 | { 38 | case '\0': 39 | case ',': 40 | case ':': 41 | case '/': 42 | value_str = param_string.substr(pos, i - pos); 43 | if (has_name && name_str.empty() && !value_str.empty()) 44 | { 45 | return F3KDB_ERROR_UNEXPECTED_END; 46 | } 47 | // Fall below 48 | if (!value_str.empty() || !name_str.empty()) { 49 | auto ret = item_callback(name_str.c_str(), value_str.c_str()); 50 | if (ret != F3KDB_SUCCESS) 51 | { 52 | return ret; 53 | } 54 | } 55 | name_str.clear(); 56 | value_str.clear(); 57 | pos = i + 1; 58 | break; 59 | case '=': 60 | if (has_name && name_str.empty()) 61 | { 62 | name_str = param_string.substr(pos, i - pos); 63 | if (name_str.empty()) 64 | { 65 | return F3KDB_ERROR_INVALID_NAME; 66 | } 67 | pos = i + 1; 68 | } 69 | break; 70 | default: 71 | // Nothing to do 72 | break; 73 | } 74 | } 75 | return F3KDB_SUCCESS; 76 | } 77 | 78 | F3KDB_API(int) f3kdb_params_fill_preset(f3kdb_params_t* params, const char* preset, int interface_version) 79 | { 80 | if (interface_version != F3KDB_INTERFACE_VERSION) 81 | { 82 | return F3KDB_ERROR_INVALID_INTERFACE_VERSION; 83 | } 84 | if (!params || !preset) 85 | { 86 | return F3KDB_ERROR_INVALID_ARGUMENT; 87 | } 88 | return parse_param_string(preset, false, [params](const char*, const char* preset_item) -> int { 89 | for (size_t i = 0; i < sizeof(PRESETS) / sizeof(PRESETS[0]) - 2; i += 2) 90 | { 91 | if (!_stricmp(preset_item, PRESETS[i])) 92 | { 93 | return f3kdb_params_fill_by_string(params, PRESETS[i + 1]); 94 | } 95 | } 96 | return (int)F3KDB_ERROR_INVALID_NAME; 97 | }); 98 | } 99 | 100 | F3KDB_API(int) f3kdb_params_fill_by_string(f3kdb_params_t* params, const char* param_string, int interface_version) 101 | { 102 | if (interface_version != F3KDB_INTERFACE_VERSION) 103 | { 104 | return F3KDB_ERROR_INVALID_INTERFACE_VERSION; 105 | } 106 | return parse_param_string(param_string, true, [=](const char* name, const char* value) { 107 | return params_set_by_string(params, name, value); 108 | }); 109 | } 110 | 111 | static void print_error(char* buffer, size_t buffer_size, const char* format, ...) 112 | { 113 | if (!buffer || buffer_size <= 0) 114 | { 115 | return; 116 | } 117 | va_list va; 118 | va_start(va, format); 119 | vsnprintf(buffer, buffer_size, format, va); 120 | } 121 | 122 | static inline bool is_out_of_range(int value, int lower_bound, int upper_bound) 123 | { 124 | // Suppress -Wtype-limits 125 | return value < lower_bound || value > upper_bound; 126 | } 127 | 128 | F3KDB_API(int) f3kdb_create(const f3kdb_video_info_t* video_info_in, const f3kdb_params_t* params_in, f3kdb_core_t** core_out, char* extra_error_msg, int error_msg_size, int interface_version) 129 | { 130 | if (interface_version != F3KDB_INTERFACE_VERSION) 131 | { 132 | return F3KDB_ERROR_INVALID_INTERFACE_VERSION; 133 | } 134 | *core_out = nullptr; 135 | if (extra_error_msg && error_msg_size > 0) 136 | { 137 | extra_error_msg[0] = 0; 138 | } 139 | 140 | #define INVALID_PARAM_IF(cond) \ 141 | do { if (cond) { print_error(extra_error_msg, error_msg_size, "Invalid parameter condition: %s", #cond); return F3KDB_ERROR_INVALID_ARGUMENT; } } while (0) 142 | 143 | INVALID_PARAM_IF(!video_info_in); 144 | INVALID_PARAM_IF(!params_in); 145 | 146 | f3kdb_video_info_t video_info; 147 | memcpy(&video_info, video_info_in, sizeof(f3kdb_video_info_t)); 148 | 149 | INVALID_PARAM_IF(video_info.width < 16); 150 | INVALID_PARAM_IF(video_info.height < 16); 151 | INVALID_PARAM_IF(video_info.chroma_width_subsampling < 0 || video_info.chroma_width_subsampling > 4); 152 | INVALID_PARAM_IF(video_info.chroma_height_subsampling < 0 || video_info.chroma_height_subsampling > 4); 153 | INVALID_PARAM_IF(video_info.num_frames <= 0); 154 | INVALID_PARAM_IF(video_info.depth < 8 || video_info.depth > INTERNAL_BIT_DEPTH); 155 | INVALID_PARAM_IF(video_info.pixel_mode < 0 || video_info.pixel_mode >= PIXEL_MODE_COUNT); 156 | INVALID_PARAM_IF(video_info.pixel_mode == LOW_BIT_DEPTH && video_info.depth != 8); 157 | INVALID_PARAM_IF(video_info.pixel_mode != LOW_BIT_DEPTH && video_info.depth == 8); 158 | 159 | f3kdb_params_t params; 160 | memcpy(¶ms, params_in, sizeof(f3kdb_params_t)); 161 | 162 | if (params.output_depth == 16) 163 | { 164 | // set to appropriate precision mode 165 | params.dither_algo = DA_16BIT_INTERLEAVED; 166 | } 167 | 168 | int threshold_upper_limit = 64 * 8 - 1; 169 | int dither_upper_limit = 4096; 170 | 171 | #define CHECK_PARAM(value, lower_bound, upper_bound) \ 172 | do { if (is_out_of_range(params.value, lower_bound, upper_bound)) { print_error(extra_error_msg, error_msg_size, "Invalid parameter %s, must be between %d and %d", #value, lower_bound, upper_bound); return F3KDB_ERROR_INVALID_ARGUMENT; } } while(0) 173 | 174 | CHECK_PARAM(range, 0, 31); 175 | CHECK_PARAM(Y, 0, threshold_upper_limit); 176 | CHECK_PARAM(Cb, 0, threshold_upper_limit); 177 | CHECK_PARAM(Cr, 0, threshold_upper_limit); 178 | CHECK_PARAM(grainY, 0, dither_upper_limit); 179 | CHECK_PARAM(grainC, 0, dither_upper_limit); 180 | CHECK_PARAM(sample_mode, 1, 2); 181 | CHECK_PARAM(opt, IMPL_AUTO_DETECT, (IMPL_COUNT - 1) ); 182 | CHECK_PARAM(dither_algo, DA_HIGH_NO_DITHERING, (DA_COUNT - 1) ); 183 | CHECK_PARAM(random_algo_ref, 0, (RANDOM_ALGORITHM_COUNT - 1) ); 184 | CHECK_PARAM(random_algo_grain, 0, (RANDOM_ALGORITHM_COUNT - 1) ); 185 | 186 | 187 | // now the internal bit depth is 16, 188 | // scale parameters to be consistent with 14bit range in previous versions 189 | params.Y <<= 2; 190 | params.Cb <<= 2; 191 | params.Cr <<= 2; 192 | params.grainY <<= 2; 193 | params.grainC <<= 2; 194 | 195 | try 196 | { 197 | *core_out = new f3kdb_core_t(&video_info, ¶ms); 198 | } catch (std::bad_alloc&) { 199 | return F3KDB_ERROR_INSUFFICIENT_MEMORY; 200 | } 201 | return F3KDB_SUCCESS; 202 | } 203 | 204 | F3KDB_API(int) f3kdb_destroy(f3kdb_core_t* core) 205 | { 206 | delete core; 207 | return F3KDB_SUCCESS; 208 | } 209 | 210 | F3KDB_API(int) f3kdb_process_plane(f3kdb_core_t* core, int frame_index, int plane, unsigned char* dst_frame_ptr, int dst_pitch, const unsigned char* src_frame_ptr, int src_pitch) 211 | { 212 | if (!core) 213 | { 214 | return F3KDB_ERROR_INVALID_ARGUMENT; 215 | } 216 | return core->process_plane(frame_index, plane, dst_frame_ptr, dst_pitch, src_frame_ptr, src_pitch); 217 | } 218 | -------------------------------------------------------------------------------- /src/random.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "random.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | 11 | typedef double (*rand_impl_t)(int& seed, double param); 12 | 13 | double rand_old(int& seed, double param); 14 | 15 | double rand_uniform(int& seed, double param); 16 | 17 | double rand_gaussian(int& seed, double param); 18 | 19 | static const rand_impl_t rand_algorithms[] = { 20 | rand_old, 21 | rand_uniform, 22 | rand_gaussian 23 | }; 24 | 25 | inline double round(double r) { 26 | return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); 27 | } 28 | 29 | int random(RANDOM_ALGORITHM algo, int& seed, int range, double param) 30 | { 31 | assert(algo >= 0 && algo < RANDOM_ALGORITHM_COUNT); 32 | 33 | double num = rand_algorithms[algo](seed, param); 34 | assert(num >= -1.0 && num <= 1.0); 35 | return (int)round(num * range); 36 | } 37 | 38 | // most algorithms below are stolen from AddGrainC 39 | 40 | double rand_to_double(int rand_num) 41 | { 42 | // convert the number to 52 bit, use high 12 bits to fill lower space 43 | // (otherwise the upper bound will be significantly less than 1.0) 44 | union 45 | { 46 | uint64_t itemp; 47 | double result; 48 | }; 49 | itemp = ((uint64_t)rand_num) & 0xffffffffULL; 50 | itemp = itemp << 20 | itemp >> 12; 51 | 52 | // fill exponent with 1 53 | itemp |= 0x3ff0000000000000ULL; 54 | 55 | // itemp is now in [1.0, 2.0), convert to [-1.0, 1.0) 56 | return (result - 1.0) * 2 - 1.0; 57 | } 58 | 59 | double rand_old(int& seed, double) 60 | { 61 | int seed_tmp = (((seed << 13) ^ (unsigned int)seed) >> 17) ^ (seed << 13) ^ seed; 62 | seed = 32 * seed_tmp ^ seed_tmp; 63 | return rand_to_double(seed); 64 | } 65 | 66 | double rand_uniform(int& seed, double) 67 | { 68 | seed = 1664525 * seed + 1013904223; 69 | return rand_to_double(seed); 70 | } 71 | 72 | // http://www.bearcave.com/misl/misl_tech/wavelets/hurst/random.html 73 | double rand_gaussian(int& seed, double param) 74 | { 75 | double ret; 76 | double x, y, r2; 77 | 78 | do 79 | { 80 | do 81 | { 82 | /* choose x,y in uniform square (-1,-1) to (+1,+1) */ 83 | 84 | x = rand_uniform (seed, param); 85 | y = rand_uniform (seed, param); 86 | 87 | /* see if it is in the unit circle */ 88 | r2 = x * x + y * y; 89 | } 90 | while (r2 > 1.0 || r2 == 0); 91 | /* Box-Muller transform */ 92 | 93 | // sigma = param 94 | ret = param * y * sqrt (-2.0 * log (r2) / r2); 95 | 96 | } while (ret <= -1.0 || ret >= 1.0); 97 | // we need to clip the result because the wrapper accepts [-1.0, 1.0] only 98 | 99 | return ret; 100 | } 101 | -------------------------------------------------------------------------------- /src/random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "constants.h" 4 | #include 5 | 6 | #define DEFAULT_RANDOM_PARAM 1.0 7 | 8 | // returns a random number in [-range, range] 9 | int random(RANDOM_ALGORITHM algo, int& seed, int range, double param); -------------------------------------------------------------------------------- /src/sse_compat.h: -------------------------------------------------------------------------------- 1 | 2 | #if !defined(SSE_LIMIT) || SSE_LIMIT >= 41 3 | 4 | #define _cmm_blendv_by_cmp_mask_epi8 _mm_blendv_epi8 5 | 6 | #define _cmm_mullo_limit16_epi32 _mm_mullo_epi32 7 | 8 | #else 9 | 10 | static __m128i __forceinline _cmm_blendv_by_cmp_mask_epi8( 11 | __m128i a, 12 | __m128i b, 13 | __m128i mask 14 | ) 15 | { 16 | // mask is always returned from cmp functions, so it can be used directly 17 | __m128i b_mask = _mm_and_si128(mask, b); 18 | __m128i a_mask = _mm_andnot_si128(mask, a); 19 | 20 | return _mm_or_si128(a_mask, b_mask); 21 | } 22 | 23 | static __m128i __forceinline _cmm_mullo_limit16_epi32(__m128i a, __m128i b) 24 | { 25 | // input can always be fit in 16bit 26 | 27 | a = _mm_packs_epi32(a, a); 28 | 29 | b = _mm_packs_epi32(b, b); 30 | 31 | __m128i lo_part = _mm_mullo_epi16(a, b); 32 | 33 | __m128i hi_part = _mm_mulhi_epi16(a, b); 34 | 35 | return _mm_unpacklo_epi16(lo_part, hi_part); 36 | } 37 | 38 | static __m128i __forceinline _mm_min_epu16(__m128i val1, __m128i val2) 39 | { 40 | __m128i sign_convert = _mm_set1_epi16((short)0x8000); 41 | val1 = _mm_sub_epi16(val1, sign_convert); 42 | val2 = _mm_sub_epi16(val2, sign_convert); 43 | 44 | return _mm_add_epi16(_mm_min_epi16(val1, val2), sign_convert); 45 | } 46 | #endif 47 | 48 | 49 | 50 | #if !defined(SSE_LIMIT) || SSE_LIMIT >= 31 51 | 52 | static __m128i __forceinline _cmm_negate_all_epi32(__m128i a, __m128i minus_one) 53 | { 54 | return _mm_sign_epi32(a, minus_one); 55 | } 56 | 57 | #else 58 | 59 | static __m128i __forceinline _cmm_negate_all_epi32(__m128i a, __m128i minus_one) 60 | { 61 | return _mm_sub_epi32(_mm_setzero_si128(), a); 62 | } 63 | 64 | static __m128i __forceinline _mm_abs_epi32 (__m128i a) 65 | { 66 | __m128i mask = _mm_srai_epi32(a, 31); 67 | __m128i fix = _mm_srli_epi32(a, 31); 68 | a = _mm_xor_si128(a, mask); 69 | return _mm_add_epi32(a, fix); 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /src/sse_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // See Intel Optimization Guide: Ch. 5.6.6.2 Clipping to an Arbitrary Unsigned Range [High, Low] 4 | // high_add = 0xffff - high 5 | // high_sub = 0xffff - high + low 6 | static __m128i __forceinline high_bit_depth_pixels_clamp(__m128i pixels, __m128i high_add, __m128i high_sub, const __m128i& low) 7 | { 8 | pixels = _mm_adds_epu16(pixels, high_add); 9 | pixels = _mm_subs_epu16(pixels, high_sub); 10 | pixels = _mm_add_epi16(pixels, low); 11 | 12 | return pixels; 13 | } 14 | 15 | 16 | // like high_bit_depth_pixels_clamp, but all values are 8bit 17 | static __m128i __forceinline low_bit_depth_pixels_clamp(__m128i pixels, __m128i high_add, __m128i high_sub, const __m128i& low) 18 | { 19 | pixels = _mm_adds_epu8(pixels, high_add); 20 | pixels = _mm_subs_epu8(pixels, high_sub); 21 | pixels = _mm_add_epi8(pixels, low); 22 | 23 | return pixels; 24 | } 25 | -------------------------------------------------------------------------------- /src/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // flash3kyuu_deband.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WIN32 4 | 5 | #include "targetver.h" 6 | 7 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 8 | // Windows Header Files: 9 | #include 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | 2 | static inline int clamp_pixel(int pixel, int min, int max) 3 | { 4 | if (pixel > max) { 5 | pixel = max; 6 | } else if (pixel < min) { 7 | pixel = min; 8 | } 9 | return pixel; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/vapoursynth/VSHelper.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Fredrik Mellbin 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #ifndef VSHELPER_H 22 | #define VSHELPER_H 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef _WIN32 29 | #include 30 | #endif 31 | #include "VapourSynth.h" 32 | 33 | // VS2010 doesn't recognize inline in c mode 34 | #if defined(_MSC_VER) && !defined(__cplusplus) 35 | #define inline _inline 36 | #endif 37 | 38 | #ifdef _WIN32 39 | #define VS_ALIGNED_MALLOC(pptr, size, alignment) *(pptr) = _aligned_malloc((size), (alignment)) 40 | #define VS_ALIGNED_FREE(ptr) _aligned_free((ptr)) 41 | #else 42 | #define VS_ALIGNED_MALLOC(pptr, size, alignment) posix_memalign((void**)(pptr), (alignment), (size)) 43 | #define VS_ALIGNED_FREE(ptr) free((ptr)) 44 | #endif 45 | 46 | #ifdef __cplusplus 47 | // A nicer templated malloc for all the C++ users out there 48 | template 49 | static inline T* vs_aligned_malloc(size_t size, size_t alignment) { 50 | #ifdef _WIN32 51 | return (T*)_aligned_malloc(size, alignment); 52 | #else 53 | void *tmp; 54 | if (posix_memalign(&tmp, alignment, size)) 55 | tmp = 0; 56 | return (T*)tmp; 57 | #endif 58 | } 59 | 60 | static inline void vs_aligned_free(void *ptr) { 61 | VS_ALIGNED_FREE(ptr); 62 | } 63 | #endif //__cplusplus 64 | 65 | // convenience function for checking if the format never changes between frames 66 | static inline int isConstantFormat(const VSVideoInfo *vi) { 67 | return vi->height > 0 && vi->width > 0 && vi->format; 68 | } 69 | 70 | // convenience function to check for if two clips have the same format (unknown/changeable will be considered the same too) 71 | static inline int isSameFormat(const VSVideoInfo *v1, const VSVideoInfo *v2) { 72 | return v1->height == v2->height && v1->width == v2->width && v1->format == v2->format; 73 | } 74 | 75 | // multiplies and divides a rational number, such as a frame duration, in place and reduces the result 76 | static inline int muldivRational(int64_t *num, int64_t *den, int64_t mul, int64_t div) { 77 | int64_t a, b; 78 | *num *= mul; 79 | *den *= div; 80 | a = *num; 81 | b = *den; 82 | while (b != 0) { 83 | int64_t t = a; 84 | a = b; 85 | b = t % b; 86 | } 87 | if (a < 0) 88 | a = -a; 89 | *num /= a; 90 | *den /= a; 91 | return 0; 92 | } 93 | 94 | // converts an int64 to int with saturation, useful to silence warnings when reading int properties among other things 95 | static inline int int64ToIntS(int64_t i) { 96 | if (i > INT_MAX) 97 | return INT_MAX; 98 | else if (i < INT_MIN) 99 | return INT_MIN; 100 | else return (int)i; 101 | } 102 | 103 | static inline void vs_bitblt(void *dstp, int dst_stride, const void *srcp, int src_stride, int row_size, int height) { 104 | if (height > 0) { 105 | if (src_stride == dst_stride && src_stride == row_size) { 106 | memcpy(dstp, srcp, row_size * height); 107 | } else { 108 | int i; 109 | uint8_t *srcp8 = (uint8_t *)srcp; 110 | uint8_t *dstp8 = (uint8_t *)dstp; 111 | for (i = 0; i < height; i++) { 112 | memcpy(dstp8, srcp8, row_size); 113 | srcp8 += src_stride; 114 | dstp8 += dst_stride; 115 | } 116 | } 117 | } 118 | } 119 | 120 | // check if the frame dimensions are valid for a given format 121 | // returns non-zero for valid width and height 122 | static inline int areValidDimensions(const VSFormat *fi, int width, int height) { 123 | return !(width % (1 << fi->subSamplingW) || height % (1 << fi->subSamplingH)); 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/vapoursynth/plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "VapourSynth.h" 2 | #include "VSHelper.h" 3 | #include "plugin.def.h" 4 | 5 | typedef struct { 6 | VSNodeRef *node; 7 | VSVideoInfo vi; 8 | f3kdb_core_t* core; 9 | } f3kdb_vs_context_t; 10 | 11 | static const int F3KDB_PLANES[] = {PLANE_Y, PLANE_CB, PLANE_CR}; 12 | 13 | static void VS_CC f3kdbInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) { 14 | f3kdb_vs_context_t *d = (f3kdb_vs_context_t *) * instanceData; 15 | vsapi->setVideoInfo(&d->vi, 1, node); 16 | } 17 | 18 | static const VSFrameRef *VS_CC f3kdbGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { 19 | f3kdb_vs_context_t *d = (f3kdb_vs_context_t *) * instanceData; 20 | 21 | if (activationReason == arInitial) { 22 | vsapi->requestFrameFilter(n, d->node, frameCtx); 23 | } else if (activationReason == arAllFramesReady) { 24 | const VSFrameRef *src = vsapi->getFrameFilter(n, d->node, frameCtx); 25 | VSFrameRef *dst = vsapi->newVideoFrame(d->vi.format, d->vi.width, d->vi.height, src, core); 26 | for (int i = 0; i < d->vi.format->numPlanes; i++) 27 | { 28 | const unsigned char* src_ptr = vsapi->getReadPtr(src, i); 29 | int src_stride = vsapi->getStride(src, i); 30 | unsigned char* dst_ptr = vsapi->getWritePtr(dst, i); 31 | int dst_stride = vsapi->getStride(dst, i); 32 | int f3kdb_plane = F3KDB_PLANES[i]; 33 | 34 | int result = f3kdb_process_plane(d->core, n, f3kdb_plane, dst_ptr, dst_stride, src_ptr, src_stride); 35 | if (result != F3KDB_SUCCESS) 36 | { 37 | char msg[1024]; 38 | memset(msg, 0, sizeof(msg)); 39 | _snprintf(msg, sizeof(msg) - 1, "f3kdb: Error while processing plane, f3kdb_plane: %d, code: %d", f3kdb_plane, result); 40 | vsapi->setFilterError(msg, frameCtx); 41 | return 0; 42 | } 43 | } 44 | vsapi->freeFrame(src); 45 | return dst; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | static void VS_CC f3kdbFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { 52 | f3kdb_vs_context_t *d = (f3kdb_vs_context_t *)instanceData; 53 | f3kdb_destroy(d->core); 54 | vsapi->freeNode(d->node); 55 | free(d); 56 | } 57 | 58 | static void VS_CC f3kdbCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) { 59 | auto node = vsapi->propGetNode(in, "clip", 0, 0); 60 | VSVideoInfo vi = *vsapi->getVideoInfo(node); 61 | 62 | if (!isConstantFormat(&vi)) 63 | { 64 | vsapi->setError(out, "f3kdb: Only constant format is supported"); 65 | vsapi->freeNode(node); 66 | return; 67 | } 68 | 69 | if (vi.format->sampleType != stInteger) 70 | { 71 | vsapi->setError(out, "f3kdb: Only integer sample format is supported"); 72 | vsapi->freeNode(node); 73 | return; 74 | } 75 | 76 | if (vi.format->bitsPerSample < 8 || vi.format->bitsPerSample > 16) 77 | { 78 | vsapi->setError(out, "f3kdb: Only 8 ~ 16 bits per sample is supported"); 79 | vsapi->freeNode(node); 80 | return; 81 | } 82 | 83 | f3kdb_params_t params; 84 | f3kdb_params_init_defaults(¶ms); 85 | 86 | int error; 87 | const char* preset = vsapi->propGetData(in, "preset", 0, &error); 88 | if (error != peUnset) 89 | { 90 | if (error == peType) 91 | { 92 | vsapi->setError(out, "f3kdb: preset must be a string"); 93 | vsapi->freeNode(node); 94 | return; 95 | } else if (error != _peNoError) { 96 | vsapi->setError(out, "f3kdb: Unknown preset error"); 97 | vsapi->freeNode(node); 98 | return; 99 | } 100 | int result = f3kdb_params_fill_preset(¶ms, preset); 101 | if (result != F3KDB_SUCCESS) 102 | { 103 | vsapi->setError(out, "f3kdb: Invalid preset"); 104 | vsapi->freeNode(node); 105 | return; 106 | } 107 | } 108 | 109 | bool success = f3kdb_params_from_vs(¶ms, in, out, vsapi); 110 | if (!success) 111 | { 112 | // Error message has been set in param_from_vsmap 113 | vsapi->freeNode(node); 114 | return; 115 | } 116 | if (params.output_depth == -1) 117 | params.output_depth = vi.format->bitsPerSample; 118 | 119 | f3kdb_video_info_t video_info; 120 | video_info.width = vi.width; 121 | video_info.height = vi.height; 122 | video_info.depth = vi.format->bitsPerSample; 123 | video_info.pixel_mode = vi.format->bitsPerSample == 8 ? LOW_BIT_DEPTH : HIGH_BIT_DEPTH_INTERLEAVED; 124 | video_info.num_frames = vi.numFrames; 125 | video_info.chroma_width_subsampling = vi.format->subSamplingW; 126 | video_info.chroma_height_subsampling = vi.format->subSamplingH; 127 | 128 | const VSFormat* new_format = vsapi->registerFormat(vi.format->colorFamily, vi.format->sampleType, params.output_depth, vi.format->subSamplingW, vi.format->subSamplingH, core); 129 | if (!new_format) 130 | { 131 | vsapi->setError(out, "f3kdb: Unable to register output format"); 132 | vsapi->freeNode(node); 133 | return; 134 | } 135 | 136 | char error_msg[1024]; 137 | memset(error_msg, 0, sizeof(error_msg)); 138 | f3kdb_core_t* f3kdb_core = nullptr; 139 | int result = f3kdb_create(&video_info, ¶ms, &f3kdb_core, error_msg, sizeof(error_msg) - 1); 140 | if (result != F3KDB_SUCCESS) 141 | { 142 | char vs_err_msg[2048]; 143 | memset(vs_err_msg, 0, sizeof(vs_err_msg)); 144 | _snprintf(vs_err_msg, sizeof(vs_err_msg) - 1, "f3kdb: Core initialization failed, code = %d. %s", result, error_msg); 145 | vsapi->setError(out, vs_err_msg); 146 | vsapi->freeNode(node); 147 | return; 148 | } 149 | 150 | f3kdb_vs_context_t* context = (f3kdb_vs_context_t*)malloc(sizeof(f3kdb_vs_context_t)); 151 | if (!context) 152 | { 153 | // Shouldn't really happen, just in case 154 | f3kdb_destroy(f3kdb_core); 155 | vsapi->freeNode(node); 156 | return; 157 | } 158 | 159 | context->core = f3kdb_core; 160 | context->node = node; 161 | context->vi = vi; 162 | context->vi.format = new_format; 163 | 164 | vsapi->createFilter(in, out, "f3kdb", f3kdbInit, f3kdbGetFrame, f3kdbFree, fmParallel, 0, context, core); 165 | return; 166 | } 167 | 168 | ////////////////////////////////////////// 169 | // Init 170 | 171 | VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) { 172 | configFunc("net.sapikachu.f3kdb", "f3kdb", "flash3kyuu_deband", VAPOURSYNTH_API_VERSION, 1, plugin); 173 | registerFunc("Deband", F3KDB_VAPOURSYNTH_PARAMS, f3kdbCreate, 0, plugin); 174 | } 175 | -------------------------------------------------------------------------------- /src/vapoursynth/plugin.def.h: -------------------------------------------------------------------------------- 1 | 2 | /************************* 3 | * Script generated code * 4 | * Do not modify * 5 | *************************/ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "plugin.h" 11 | #include "VapourSynth.h" 12 | 13 | static const char* F3KDB_VAPOURSYNTH_PARAMS = "clip:clip;range:int:opt;y:int:opt;cb:int:opt;cr:int:opt;grainy:int:opt;grainc:int:opt;sample_mode:int:opt;seed:int:opt;blur_first:int:opt;dynamic_grain:int:opt;opt:int:opt;dither_algo:int:opt;keep_tv_range:int:opt;output_depth:int:opt;random_algo_ref:int:opt;random_algo_grain:int:opt;random_param_ref:float:opt;random_param_grain:float:opt;preset:data:opt;"; 14 | 15 | static bool f3kdb_params_from_vs(f3kdb_params_t* f3kdb_params, const VSMap* in, VSMap* out, const VSAPI* vsapi) 16 | { 17 | if (!param_from_vsmap(&f3kdb_params->range, "range", in, out, vsapi)) { return false; } 18 | if (!param_from_vsmap(&f3kdb_params->Y, "y", in, out, vsapi)) { return false; } 19 | if (!param_from_vsmap(&f3kdb_params->Cb, "cb", in, out, vsapi)) { return false; } 20 | if (!param_from_vsmap(&f3kdb_params->Cr, "cr", in, out, vsapi)) { return false; } 21 | if (!param_from_vsmap(&f3kdb_params->grainY, "grainy", in, out, vsapi)) { return false; } 22 | if (!param_from_vsmap(&f3kdb_params->grainC, "grainc", in, out, vsapi)) { return false; } 23 | if (!param_from_vsmap(&f3kdb_params->sample_mode, "sample_mode", in, out, vsapi)) { return false; } 24 | if (!param_from_vsmap(&f3kdb_params->seed, "seed", in, out, vsapi)) { return false; } 25 | if (!param_from_vsmap(&f3kdb_params->blur_first, "blur_first", in, out, vsapi)) { return false; } 26 | if (!param_from_vsmap(&f3kdb_params->dynamic_grain, "dynamic_grain", in, out, vsapi)) { return false; } 27 | if (!param_from_vsmap(&f3kdb_params->opt, "opt", in, out, vsapi)) { return false; } 28 | if (!param_from_vsmap(&f3kdb_params->dither_algo, "dither_algo", in, out, vsapi)) { return false; } 29 | if (!param_from_vsmap(&f3kdb_params->keep_tv_range, "keep_tv_range", in, out, vsapi)) { return false; } 30 | if (!param_from_vsmap(&f3kdb_params->output_depth, "output_depth", in, out, vsapi)) { return false; } 31 | if (!param_from_vsmap(&f3kdb_params->random_algo_ref, "random_algo_ref", in, out, vsapi)) { return false; } 32 | if (!param_from_vsmap(&f3kdb_params->random_algo_grain, "random_algo_grain", in, out, vsapi)) { return false; } 33 | if (!param_from_vsmap(&f3kdb_params->random_param_ref, "random_param_ref", in, out, vsapi)) { return false; } 34 | if (!param_from_vsmap(&f3kdb_params->random_param_grain, "random_param_grain", in, out, vsapi)) { return false; } 35 | return true; 36 | } 37 | -------------------------------------------------------------------------------- /src/vapoursynth/plugin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include "VapourSynth.h" 10 | #include "../compiler_compat.h" 11 | 12 | static const int _peOutOfRange = 0x7fffffff; 13 | static const int _peNoError = 0; 14 | 15 | template 16 | static T get_value_from_vsmap(const VSAPI* vsapi, const VSMap* in, const char* name, int* error) 17 | { 18 | using namespace std; 19 | 20 | int64_t raw_int = vsapi->propGetInt(in, name, 0, error); 21 | if (*error == _peNoError && !is_enum::value && (raw_int < numeric_limits::min() || raw_int > numeric_limits::max())) 22 | { 23 | *error = _peOutOfRange; 24 | return (T)0; 25 | } 26 | return (T)raw_int; 27 | } 28 | 29 | template <> 30 | inline bool get_value_from_vsmap(const VSAPI* vsapi, const VSMap* in, const char* name, int* error) 31 | { 32 | return !!vsapi->propGetInt(in, name, 0, error); 33 | } 34 | 35 | template <> 36 | inline double get_value_from_vsmap(const VSAPI* vsapi, const VSMap* in, const char* name, int* error) 37 | { 38 | return vsapi->propGetFloat(in, name, 0, error); 39 | } 40 | 41 | template 42 | inline bool param_from_vsmap(T* target, const char* name, const VSMap* in, VSMap* out, const VSAPI* vsapi) 43 | { 44 | using namespace std; 45 | 46 | static_assert(is_integral::value || is_same::value || is_enum::value, "Unsupported type"); 47 | 48 | int error = _peNoError; 49 | 50 | auto _set_error = [=](const char* message) { 51 | char error_msg[1024]; 52 | memset(error_msg, 0, sizeof(error_msg)); 53 | _snprintf(error_msg, sizeof(error_msg) - 1, message, name, error); 54 | vsapi->setError(out, error_msg); 55 | }; 56 | 57 | T value = get_value_from_vsmap(vsapi, in, name, &error); 58 | switch (error) 59 | { 60 | case _peNoError: 61 | break; 62 | case _peOutOfRange: 63 | _set_error("f3kdb: Argument out of range: %s"); 64 | return false; 65 | case peUnset: 66 | // Optional parameter 67 | return true; 68 | case peType: 69 | _set_error("f3kdb: Invalid argument type: %s"); 70 | return false; 71 | default: 72 | _set_error("f3kdb: Unknown argument error: %s (code %d)"); 73 | return false; 74 | break; 75 | } 76 | *target = value; 77 | return true; 78 | } -------------------------------------------------------------------------------- /src/wscript: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import waflib 4 | 5 | 6 | def build(bld): 7 | gen_filter_def = bld.path.find_node("gen_filter_def.py") 8 | gen_output = bld.cmd_and_log( 9 | [bld.env["PYTHON3"][0], gen_filter_def.abspath(), "--list-outputs"], 10 | quiet=waflib.Context.BOTH, 11 | ) 12 | gen_output_list = re.split(r"\s+", gen_output.strip(), flags=re.S) 13 | 14 | bld( 15 | rule="${PYTHON3} ${SRC[0].abspath()}", 16 | source=gen_filter_def, 17 | target=[bld.path.find_node(x) for x in gen_output_list], 18 | cwd=bld.path.abspath(), 19 | ) 20 | bld( 21 | features="cxx", 22 | source=bld.path.ant_glob( 23 | ["*.cpp"] + 24 | (["vapoursynth/*.cpp"] if bld.env.ENABLE_VAPOURSYNTH else []), 25 | excl=[ 26 | "dllmain.cpp", 27 | "icc_override.cpp", 28 | "stdafx.cpp", 29 | "debug_dump.cpp", 30 | "flash3kyuu_deband_impl_ssse3.cpp", 31 | "flash3kyuu_deband_impl_sse2.cpp", 32 | "flash3kyuu_deband_impl_sse4.cpp", 33 | ], 34 | ), 35 | target="f3kdb-objs", 36 | ) 37 | bld( 38 | features="cxx", 39 | source="flash3kyuu_deband_impl_sse2.cpp", 40 | target="f3kdb-impl-sse2", 41 | cxxflags=["-msse2"], 42 | ) 43 | bld( 44 | features="cxx", 45 | source="flash3kyuu_deband_impl_ssse3.cpp", 46 | target="f3kdb-impl-ssse3", 47 | cxxflags=["-mssse3"], 48 | ) 49 | bld( 50 | features="cxx", 51 | source="flash3kyuu_deband_impl_sse4.cpp", 52 | target="f3kdb-impl-sse4", 53 | cxxflags=["-msse4.1"], 54 | ) 55 | -------------------------------------------------------------------------------- /src/x64_compat.h: -------------------------------------------------------------------------------- 1 | #include 2 | typedef intptr_t POINTER_INT; 3 | -------------------------------------------------------------------------------- /test/build_core_param_set.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | import glob 6 | from itertools import product 7 | 8 | HEADER = r""" 9 | /************************* 10 | * Script generated code * 11 | * Do not modify * 12 | *************************/ 13 | 14 | #pragma once 15 | 16 | #include "../include/f3kdb.h" 17 | 18 | """ 19 | 20 | NAME_PARSER = re.compile( 21 | r"^(?P\w+)_(?P\d+)x(?P\d+)_" 22 | r"(?P\d)_(?P\d)_(?P\d)_(?P\d+)\.yuv$", 23 | re.I, 24 | ) 25 | 26 | 27 | def process_file(name): 28 | base_name = os.path.basename(name) 29 | var_name = re.sub("[^A-Za-z0-9]", "_", os.path.splitext(base_name)[0]) 30 | m = NAME_PARSER.match(base_name) 31 | print((r"f3kdb_video_info_t frame_{var_name}_vi = " 32 | r"{{ {width}, {height}, {w_subsamp}, {h_subsamp}, " 33 | r"(PIXEL_MODE){mode}, {depth}, 0 }};") 34 | .format(var_name=var_name, **m.groupdict())) 35 | 36 | with open(name, "rb") as f: 37 | print("const unsigned char frame_{}_data[] = {{ {} }};".format( 38 | var_name, 39 | ", ".join("0x{:x}".format(x) for x in f.read()), 40 | )) 41 | 42 | 43 | print(("const case_frame_t frame_{name} = " 44 | "{{ frame_{name}_vi, frame_{name}_data }};").format(name=var_name)) 45 | 46 | return var_name 47 | 48 | 49 | def convert_param_item(item): 50 | if isinstance(item, str): 51 | return item 52 | 53 | keys, value = item 54 | return "/".join("{}={}".format(x, value) for x in keys) 55 | 56 | 57 | def generate_param_set(): 58 | params = ( 59 | list(product( 60 | (("y", "cb", "cr",),), 61 | (64, 32, 0, 96), 62 | )), 63 | list(product( 64 | (("grainy", "grainc",),), 65 | (64, 32, 0), 66 | )), 67 | ( 68 | "range=15/keep_tv_range=true", 69 | "range=31/keep_tv_range=false", 70 | ), 71 | ( 72 | "sample_mode=2/blur_first=true", 73 | "sample_mode=2/blur_first=false", 74 | "sample_mode=1/blur_first=true", 75 | ), 76 | ( 77 | "output_depth=8/dither_algo=1", 78 | "output_depth=8/dither_algo=2", 79 | "output_depth=8/dither_algo=3", 80 | "output_depth=10/output_mode=1/dither_algo=1", 81 | "output_depth=10/output_mode=1/dither_algo=2", 82 | "output_depth=10/output_mode=1/dither_algo=3", 83 | "output_depth=10/output_mode=2/dither_algo=1", 84 | "output_depth=10/output_mode=2/dither_algo=2", 85 | "output_depth=10/output_mode=2/dither_algo=3", 86 | "output_depth=16/output_mode=1", 87 | "output_depth=16/output_mode=2", 88 | ), 89 | ) 90 | print("const char* param_set[] = {") 91 | print(",\n".join([ 92 | '"{}"'.format("/".join(convert_param_item(item) for item in group)) 93 | for group in product(*params) 94 | ])) 95 | print("};") 96 | 97 | 98 | def main(): 99 | print(HEADER) 100 | frames = [] 101 | for name in sorted(glob.iglob(os.path.join("case_frames", "*.yuv"))): 102 | frames.append(process_file(name)) 103 | 104 | print("const case_frame_t* frames[] = {{ {} }};".format(", ".join( 105 | "&frame_" + x for x in frames 106 | ))) 107 | generate_param_set() 108 | 109 | 110 | if __name__ == '__main__': 111 | main() 112 | -------------------------------------------------------------------------------- /test/case_frames/wallorig_320x180_1_1_1_10.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/test/case_frames/wallorig_320x180_1_1_1_10.yuv -------------------------------------------------------------------------------- /test/case_frames/wallorig_320x180_1_1_2_10.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/test/case_frames/wallorig_320x180_1_1_2_10.yuv -------------------------------------------------------------------------------- /test/case_frames/wallorig_320x180_1_1_2_16.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/test/case_frames/wallorig_320x180_1_1_2_16.yuv -------------------------------------------------------------------------------- /test/case_frames/wallorig_640x360_1_1_0_8.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/test/case_frames/wallorig_640x360_1_1_0_8.yuv -------------------------------------------------------------------------------- /test/case_frames/wallorignoise_320x180_1_1_1_16.yuv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/test/case_frames/wallorignoise_320x180_1_1_1_16.yuv -------------------------------------------------------------------------------- /test/gtest_stub.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__clang__) 4 | #pragma clang diagnostic push 5 | #pragma clang diagnostic ignored "-Wsign-compare" 6 | #include 7 | #pragma clang diagnostic pop 8 | #elif defined(__GNUC__) 9 | #pragma GCC diagnostic push 10 | #pragma GCC diagnostic ignored "-Wsign-compare" 11 | #include 12 | #pragma GCC diagnostic pop 13 | #else 14 | #include 15 | #endif 16 | -------------------------------------------------------------------------------- /test/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // f3kdb_test.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /test/stdafx.h: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #pragma once 3 | 4 | #include "targetver.h" 5 | 6 | #include 7 | #include 8 | #endif 9 | -------------------------------------------------------------------------------- /test/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /test/test_core.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | #include "gtest_stub.h" 5 | #include 6 | #include "../src/compiler_compat.h" 7 | 8 | using namespace testing; 9 | using namespace std; 10 | 11 | static const int TEST_ROUNDS = 2; 12 | 13 | static const int GUARD_CONST = 0xDEADBEEF; 14 | 15 | static unsigned char * create_guarded_buffer(int const height, int const pitch, unsigned char **data_start) 16 | { 17 | assert(pitch % PLANE_ALIGNMENT == 0); 18 | unsigned char* buffer = (unsigned char*)_aligned_malloc( (height + 4) * pitch, PLANE_ALIGNMENT); 19 | 20 | for (int i = 0; i < pitch * 2; i += 4) 21 | { 22 | *(int*)(buffer + i) = GUARD_CONST; 23 | } 24 | for (int i = 0; i < pitch * 2; i += 4) 25 | { 26 | *(int*)(buffer + (height + 2) * pitch + i) = GUARD_CONST; 27 | } 28 | *data_start = buffer + pitch * 2; 29 | return buffer; 30 | } 31 | 32 | static void check_guard_bytes(unsigned char *buffer, int const height, int const pitch) 33 | { 34 | { 35 | SCOPED_TRACE("Leading guard bytes was overwritten"); 36 | for (int i = 0; i < pitch * 2; i += 4) 37 | { 38 | ASSERT_EQ(GUARD_CONST, *(int*)(buffer + i)); 39 | } 40 | } 41 | { 42 | SCOPED_TRACE("Trailing guard bytes was overwritten"); 43 | for (int i = 0; i < pitch * 2; i += 4) 44 | { 45 | ASSERT_EQ(GUARD_CONST, *(int*)(buffer + (height + 2) * pitch + i)); 46 | } 47 | } 48 | } 49 | 50 | typedef struct _case_frame_t { 51 | f3kdb_video_info_t video_info; 52 | const unsigned char* frame_data; 53 | } case_frame_t; 54 | 55 | template 56 | class AlignedMemoryDeleter 57 | { 58 | public: 59 | void operator() (T*& ptr) 60 | { 61 | _aligned_free(ptr); 62 | ptr = nullptr; 63 | } 64 | }; 65 | 66 | class F3kdbCoreDeleter 67 | { 68 | public: 69 | void operator() (f3kdb_core_t*& ptr) 70 | { 71 | if (!ptr) { 72 | return; 73 | } 74 | int ret = f3kdb_destroy(ptr); 75 | ptr = nullptr; 76 | assert(ret == F3KDB_SUCCESS); 77 | } 78 | }; 79 | 80 | typedef unique_ptr< unsigned char, AlignedMemoryDeleter > aligned_buffer_ptr; 81 | typedef unique_ptr< f3kdb_core_t, F3kdbCoreDeleter > f3kdb_core_ptr; 82 | 83 | class CoreTest : public TestWithParam< tuple > { 84 | protected: 85 | virtual void SetUp() { 86 | int ret; 87 | ret = f3kdb_params_init_defaults(&_params); 88 | ASSERT_EQ(F3KDB_SUCCESS, ret); 89 | const char* param_string = nullptr; 90 | const case_frame_t* case_frame = nullptr; 91 | tie(param_string, case_frame) = GetParam(); 92 | _frame_data = case_frame->frame_data; 93 | _video_info = case_frame->video_info; 94 | _video_info.num_frames = TEST_ROUNDS; 95 | ret = f3kdb_params_fill_by_string(&_params, param_string); 96 | ASSERT_EQ(F3KDB_SUCCESS, ret); 97 | } 98 | public: 99 | CoreTest() : _frame_data(nullptr) {} 100 | private: 101 | f3kdb_video_info_t _video_info; 102 | f3kdb_params_t _params; 103 | const unsigned char* _frame_data; 104 | 105 | int get_default_pitch(int width) { 106 | return (width + (PLANE_ALIGNMENT - 1)) & ~(PLANE_ALIGNMENT - 1); 107 | } 108 | 109 | void assert_eq_plane(const unsigned char* reference_plane, const unsigned char* test_plane, int pitch, int width, int height) { 110 | for (auto row = 0; row < height; row++) { 111 | auto offset = row * pitch; 112 | ASSERT_EQ(0, memcmp(reference_plane + offset, test_plane + offset, width)); 113 | } 114 | } 115 | 116 | void test_plane_impl(const unsigned char* src_data, int src_pitch, f3kdb_core_t* core, int plane, aligned_buffer_ptr* buffer_out, const unsigned char** reference_data_start) { 117 | // true: Compare output plane with reference plane 118 | // false: Build reference plane 119 | bool check_plane = !buffer_out; 120 | ASSERT_EQ(check_plane, !!*reference_data_start); 121 | 122 | int plane_height = _video_info.get_plane_height(plane); 123 | int plane_width = _video_info.get_plane_width(plane); 124 | int w_mul = _params.output_mode == HIGH_BIT_DEPTH_INTERLEAVED ? 2 : 1; 125 | int h_mul = _params.output_mode == HIGH_BIT_DEPTH_STACKED ? 2 : 1; 126 | int plane_height_raw = plane_height * h_mul; 127 | int plane_width_raw = plane_width * w_mul; 128 | int dst_pitch = get_default_pitch(plane_width_raw); 129 | ASSERT_GE(dst_pitch, plane_width_raw); 130 | aligned_buffer_ptr buffers[TEST_ROUNDS]; 131 | unsigned char* start_ptrs[TEST_ROUNDS] = {nullptr}; 132 | int ret; 133 | 134 | for (auto i = 0; i < TEST_ROUNDS; i++) { 135 | buffers[i].reset(create_guarded_buffer(plane_height_raw, dst_pitch, &start_ptrs[i])); 136 | ret = f3kdb_process_plane(core, i, plane, start_ptrs[i], dst_pitch, src_data, src_pitch); 137 | ASSERT_EQ(F3KDB_SUCCESS, ret); 138 | ASSERT_NO_FATAL_FAILURE(check_guard_bytes(buffers[i].get(), plane_height_raw, dst_pitch)); 139 | if (check_plane) { 140 | assert_eq_plane(*reference_data_start, start_ptrs[i], dst_pitch, plane_width_raw, plane_height_raw); 141 | } 142 | } 143 | 144 | if (!check_plane) { 145 | // Make sure output plane from all invocations are the same 146 | for (auto i = 1; i < TEST_ROUNDS; i++) { 147 | assert_eq_plane(start_ptrs[0], start_ptrs[i], dst_pitch, plane_width_raw, plane_height_raw); 148 | } 149 | buffer_out->swap(buffers[0]); 150 | *reference_data_start = start_ptrs[0]; 151 | } 152 | } 153 | 154 | void prepare_src_data(int plane, aligned_buffer_ptr* buffer_out, const unsigned char** data_start_out, int* src_pitch_ptr, int alignment_offset = 0) { 155 | ASSERT_NE(nullptr, buffer_out); 156 | ASSERT_NE(nullptr, data_start_out); 157 | ASSERT_NE(nullptr, src_pitch_ptr); 158 | 159 | int plane_height = _video_info.get_plane_height(plane); 160 | int plane_width = _video_info.get_plane_width(plane); 161 | int w_mul = _video_info.pixel_mode == HIGH_BIT_DEPTH_INTERLEAVED ? 2 : 1; 162 | int h_mul = _video_info.pixel_mode == HIGH_BIT_DEPTH_STACKED ? 2 : 1; 163 | int plane_height_raw = plane_height * h_mul; 164 | int plane_width_raw = plane_width * w_mul; 165 | if (!*src_pitch_ptr) { 166 | *src_pitch_ptr = get_default_pitch(plane_width_raw); 167 | } 168 | ASSERT_GE(*src_pitch_ptr, plane_width_raw); 169 | buffer_out->reset((unsigned char*)_aligned_malloc(*src_pitch_ptr * plane_height_raw + alignment_offset, PLANE_ALIGNMENT)); 170 | auto data_start = buffer_out->get() + alignment_offset; 171 | auto source_data = _frame_data; 172 | if (plane == PLANE_CB || plane == PLANE_CR) { 173 | source_data += _video_info.width * w_mul * _video_info.height * h_mul; 174 | if (plane == PLANE_CR) { 175 | source_data += plane_width_raw * plane_height_raw; 176 | } 177 | } 178 | 179 | for (auto i = 0; i < plane_height_raw; i++) { 180 | memcpy(data_start + *src_pitch_ptr * i, source_data + plane_width_raw * i, plane_width_raw); 181 | } 182 | 183 | *data_start_out = data_start; 184 | } 185 | 186 | 187 | protected: 188 | void do_core_check(int alignment_offset = 0, int src_pitch_offset = 0) { 189 | f3kdb_core_ptr cores[IMPL_COUNT]; 190 | static_assert(IMPL_C == 0, "We assumed IMPL_C == 0 here, fix it!"); 191 | for (OPTIMIZATION_MODE opt = IMPL_C; opt < IMPL_COUNT; opt = (OPTIMIZATION_MODE)(opt + 1)) { 192 | _params.opt = opt; 193 | f3kdb_core_t* core_out = nullptr; 194 | char error_msg[2048]; 195 | memset(error_msg, 0, sizeof(error_msg)); 196 | int result = f3kdb_create(&_video_info, &_params, &core_out, error_msg, sizeof(error_msg) - 1); 197 | ASSERT_EQ(F3KDB_SUCCESS, result) << error_msg; 198 | ASSERT_NE(nullptr, core_out); 199 | cores[opt].reset(core_out); 200 | } 201 | const int planes[] = {PLANE_Y, PLANE_CB, PLANE_CR}; 202 | char scoped_trace_text[2048]; 203 | memset(scoped_trace_text, 0, sizeof(scoped_trace_text)); 204 | for (int i = 0; i < (int)(sizeof(planes) / sizeof(planes[0])); i++) { 205 | int plane = planes[i]; 206 | _snprintf(scoped_trace_text, sizeof(scoped_trace_text) - 1, "i = %d, plane = 0x%x", i, plane); 207 | SCOPED_TRACE(scoped_trace_text); 208 | 209 | int src_pitch = 0; 210 | aligned_buffer_ptr src_buffer; 211 | const unsigned char* src_data_start = nullptr; 212 | 213 | auto prepare_src = [&] { 214 | src_buffer.reset(); 215 | src_data_start = nullptr; 216 | ASSERT_NO_FATAL_FAILURE(prepare_src_data(plane, &src_buffer, &src_data_start, &src_pitch, alignment_offset)); 217 | }; 218 | // We need to make sure src_pitch is calculated before checking src_pitch_offset 219 | ASSERT_NO_FATAL_FAILURE(prepare_src()); 220 | ASSERT_GT(src_pitch, 0); 221 | 222 | if (src_pitch_offset) { 223 | src_pitch += src_pitch_offset; 224 | ASSERT_NO_FATAL_FAILURE(prepare_src()); 225 | } 226 | 227 | auto run_test = [&] { 228 | aligned_buffer_ptr reference_buffer; 229 | const unsigned char* reference_data_start = nullptr; 230 | ASSERT_NO_FATAL_FAILURE(test_plane_impl(src_data_start, src_pitch, cores[IMPL_C].get(), plane, &reference_buffer, &reference_data_start)); 231 | for (OPTIMIZATION_MODE opt = (OPTIMIZATION_MODE)(IMPL_C + 1); opt < IMPL_COUNT; opt = (OPTIMIZATION_MODE)(opt + 1)) { 232 | _snprintf(scoped_trace_text, sizeof(scoped_trace_text) - 1, "opt = %d", opt); 233 | SCOPED_TRACE(scoped_trace_text); 234 | ASSERT_NO_FATAL_FAILURE(test_plane_impl(src_data_start, src_pitch, cores[opt].get(), plane, nullptr, &reference_data_start)); 235 | } 236 | }; 237 | { 238 | SCOPED_TRACE("Test #1"); 239 | ASSERT_NO_FATAL_FAILURE(run_test()); 240 | } 241 | { 242 | // Should work even after pitch changes 243 | SCOPED_TRACE("Test #2 "); 244 | src_pitch++; 245 | ASSERT_NO_FATAL_FAILURE(prepare_src()); 246 | ASSERT_NO_FATAL_FAILURE(run_test()); 247 | } 248 | } 249 | } 250 | 251 | }; 252 | 253 | TEST_P(CoreTest, CoreCheckAligned) { 254 | do_core_check(); 255 | } 256 | 257 | TEST_P(CoreTest, CoreCheckUnaligned) { 258 | do_core_check(1); 259 | } 260 | 261 | TEST_P(CoreTest, CoreCheckUnalignedPitch) { 262 | do_core_check(0, PLANE_ALIGNMENT - 1); 263 | } 264 | 265 | #include "test_core_param_set.h" 266 | 267 | INSTANTIATE_TEST_CASE_P(Core, CoreTest, Combine( 268 | ValuesIn(param_set), ValuesIn(frames) 269 | )); -------------------------------------------------------------------------------- /test/test_params_from_string.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include 4 | 5 | #include "gtest_stub.h" 6 | 7 | #include 8 | 9 | using namespace testing; 10 | using namespace std; 11 | 12 | static_assert(is_sameY)>::value, "f3kdb_params_t::Y should be unsigned short, or tests need to be fixed."); 13 | 14 | TEST(ParamsFromStringTest, SingleNumber) { 15 | f3kdb_params_t params; 16 | f3kdb_params_init_defaults(¶ms); 17 | params.Y = -1; 18 | int ret; 19 | ret = f3kdb_params_fill_by_string(¶ms, "Y=42"); 20 | ASSERT_EQ(F3KDB_SUCCESS, ret); 21 | ASSERT_EQ(params.Y, 42); 22 | } 23 | 24 | TEST(ParamsFromStringTest, NameShouldBeCaseInsensitive) { 25 | f3kdb_params_t params; 26 | f3kdb_params_init_defaults(¶ms); 27 | params.Y = -1; 28 | int ret; 29 | ret = f3kdb_params_fill_by_string(¶ms, "y=42"); 30 | ASSERT_EQ(F3KDB_SUCCESS, ret); 31 | ASSERT_EQ(params.Y, 42); 32 | } 33 | 34 | TEST(ParamsFromStringTest, ShouldFailOnInvalidNumber) { 35 | f3kdb_params_t params; 36 | f3kdb_params_init_defaults(¶ms); 37 | int ret; 38 | ret = f3kdb_params_fill_by_string(¶ms, "y=dummy"); 39 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 40 | ret = f3kdb_params_fill_by_string(¶ms, "y=dummy42"); 41 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 42 | ret = f3kdb_params_fill_by_string(¶ms, "y=42dummy"); 43 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 44 | } 45 | 46 | TEST(ParamsFromStringTest, ShouldFailOnOverflow) { 47 | f3kdb_params_t params; 48 | f3kdb_params_init_defaults(¶ms); 49 | int ret; 50 | ret = f3kdb_params_fill_by_string(¶ms, "y=-1"); 51 | ASSERT_EQ(F3KDB_ERROR_VALUE_OUT_OF_RANGE, ret); 52 | ret = f3kdb_params_fill_by_string(¶ms, "y=65536"); 53 | ASSERT_EQ(F3KDB_ERROR_VALUE_OUT_OF_RANGE, ret); 54 | } 55 | 56 | TEST(ParamsFromStringTest, Double) { 57 | f3kdb_params_t params; 58 | f3kdb_params_init_defaults(¶ms); 59 | int ret; 60 | ret = f3kdb_params_fill_by_string(¶ms, "random_param_grain=5.5"); 61 | ASSERT_EQ(F3KDB_SUCCESS, ret); 62 | ASSERT_EQ(params.random_param_grain, 5.5); 63 | } 64 | 65 | TEST(ParamsFromStringTest, Bool) { 66 | f3kdb_params_t params; 67 | f3kdb_params_init_defaults(¶ms); 68 | int ret; 69 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=true"); 70 | ASSERT_EQ(F3KDB_SUCCESS, ret); 71 | ASSERT_TRUE(params.keep_tv_range); 72 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=1"); 73 | ASSERT_EQ(F3KDB_SUCCESS, ret); 74 | ASSERT_TRUE(params.keep_tv_range); 75 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=false"); 76 | ASSERT_EQ(F3KDB_SUCCESS, ret); 77 | ASSERT_FALSE(params.keep_tv_range); 78 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=0"); 79 | ASSERT_EQ(F3KDB_SUCCESS, ret); 80 | ASSERT_FALSE(params.keep_tv_range); 81 | 82 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=42"); 83 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 84 | ret = f3kdb_params_fill_by_string(¶ms, "keep_tv_range=dummy"); 85 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 86 | } 87 | 88 | TEST(ParamsFromStringTest, Enum) { 89 | f3kdb_params_t params; 90 | f3kdb_params_init_defaults(¶ms); 91 | int ret; 92 | ret = f3kdb_params_fill_by_string(¶ms, "dither_algo=2"); 93 | ASSERT_EQ(F3KDB_SUCCESS, ret); 94 | ASSERT_EQ(DA_HIGH_ORDERED_DITHERING, params.dither_algo); 95 | } 96 | 97 | TEST(ParamsFromStringTest, MultiValues) { 98 | f3kdb_params_t params; 99 | f3kdb_params_init_defaults(¶ms); 100 | int ret; 101 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb=2:cr=4,keep_tv_range=true/random_param_grain=5.5"); 102 | ASSERT_EQ(F3KDB_SUCCESS, ret); 103 | ASSERT_EQ(1, params.Y); 104 | ASSERT_EQ(2, params.Cb); 105 | ASSERT_EQ(4, params.Cr); 106 | ASSERT_TRUE(params.keep_tv_range); 107 | ASSERT_EQ(params.random_param_grain, 5.5); 108 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb=2,:cr=4,/keep_tv_range=true:/,random_param_grain=5.5/,"); 109 | ASSERT_EQ(F3KDB_SUCCESS, ret); 110 | ASSERT_EQ(1, params.Y); 111 | ASSERT_EQ(2, params.Cb); 112 | ASSERT_EQ(4, params.Cr); 113 | ASSERT_TRUE(params.keep_tv_range); 114 | ASSERT_EQ(params.random_param_grain, 5.5); 115 | } 116 | 117 | TEST(ParamsFromStringTest, MultiValuesFailUnexpectedEnd) { 118 | f3kdb_params_t params; 119 | f3kdb_params_init_defaults(¶ms); 120 | int ret; 121 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb=2:cr"); 122 | ASSERT_EQ(F3KDB_ERROR_UNEXPECTED_END, ret); 123 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb:cr=2"); 124 | ASSERT_EQ(F3KDB_ERROR_UNEXPECTED_END, ret); 125 | } 126 | 127 | TEST(ParamsFromStringTest, MultiValuesFailInvalidValue) { 128 | f3kdb_params_t params; 129 | f3kdb_params_init_defaults(¶ms); 130 | int ret; 131 | ret = f3kdb_params_fill_by_string(¶ms, "y=dummy/cb=2:cr=4"); 132 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 133 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb=dummy:cr=4"); 134 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 135 | ret = f3kdb_params_fill_by_string(¶ms, "y=1/cb=2:cr=dummy"); 136 | ASSERT_EQ(F3KDB_ERROR_INVALID_VALUE, ret); 137 | } 138 | 139 | TEST(ParamsFromStringTest, Preset) { 140 | f3kdb_params_t params; 141 | f3kdb_params_init_defaults(¶ms); 142 | int ret; 143 | ret = f3kdb_params_fill_preset(¶ms, "dummy"); 144 | ASSERT_EQ(F3KDB_ERROR_INVALID_NAME, ret); 145 | ret = f3kdb_params_fill_preset(¶ms, "depth/dummy"); 146 | ASSERT_EQ(F3KDB_ERROR_INVALID_NAME, ret); 147 | ret = f3kdb_params_fill_preset(¶ms, "dummy/depth"); 148 | ASSERT_EQ(F3KDB_ERROR_INVALID_NAME, ret); 149 | 150 | params.seed = 0xDEADBEEF; 151 | ret = f3kdb_params_fill_preset(¶ms, "depth"); 152 | ASSERT_EQ(F3KDB_SUCCESS, ret); 153 | ASSERT_EQ(0xDEADBEEF, params.seed); 154 | ASSERT_EQ(0, params.Y); 155 | ASSERT_EQ(0, params.Cb); 156 | ASSERT_EQ(0, params.Cr); 157 | ASSERT_EQ(0, params.grainY); 158 | ASSERT_EQ(0, params.grainC); 159 | 160 | ret = f3kdb_params_fill_preset(¶ms, "depth"); 161 | ASSERT_EQ(F3KDB_SUCCESS, ret); 162 | ret = f3kdb_params_fill_preset(¶ms, "low"); 163 | ASSERT_EQ(F3KDB_SUCCESS, ret); 164 | ASSERT_EQ(0xDEADBEEF, params.seed); 165 | ASSERT_NE(0, params.Y); 166 | ASSERT_NE(0, params.Cb); 167 | ASSERT_NE(0, params.Cr); 168 | ASSERT_NE(0, params.grainY); 169 | ASSERT_NE(0, params.grainC); 170 | 171 | f3kdb_params_init_defaults(¶ms); 172 | ASSERT_NE(0, params.Y); 173 | params.seed = 0xDEADBEEF; 174 | ret = f3kdb_params_fill_preset(¶ms, "nograin"); 175 | ASSERT_EQ(F3KDB_SUCCESS, ret); 176 | ASSERT_EQ(0xDEADBEEF, params.seed); 177 | ASSERT_NE(0, params.Y); 178 | ASSERT_EQ(0, params.grainY); 179 | ASSERT_EQ(0, params.grainC); 180 | 181 | ret = f3kdb_params_fill_preset(¶ms, "depth"); 182 | ASSERT_EQ(F3KDB_SUCCESS, ret); 183 | ret = f3kdb_params_fill_preset(¶ms, "low/nograin"); 184 | ASSERT_EQ(F3KDB_SUCCESS, ret); 185 | ASSERT_EQ(0xDEADBEEF, params.seed); 186 | ASSERT_NE(0, params.Y); 187 | ASSERT_NE(0, params.Cb); 188 | ASSERT_NE(0, params.Cr); 189 | ASSERT_EQ(0, params.grainY); 190 | ASSERT_EQ(0, params.grainC); 191 | 192 | ret = f3kdb_params_fill_preset(¶ms, "nograin/low"); 193 | ASSERT_EQ(F3KDB_SUCCESS, ret); 194 | ASSERT_EQ(0xDEADBEEF, params.seed); 195 | ASSERT_NE(0, params.Y); 196 | ASSERT_NE(0, params.Cb); 197 | ASSERT_NE(0, params.Cr); 198 | ASSERT_NE(0, params.grainY); 199 | ASSERT_NE(0, params.grainC); 200 | 201 | ret = f3kdb_params_fill_preset(¶ms, "high/luma"); 202 | ASSERT_EQ(F3KDB_SUCCESS, ret); 203 | ASSERT_EQ(0xDEADBEEF, params.seed); 204 | ASSERT_NE(0, params.Y); 205 | ASSERT_EQ(0, params.Cb); 206 | ASSERT_EQ(0, params.Cr); 207 | ASSERT_NE(0, params.grainY); 208 | ASSERT_EQ(0, params.grainC); 209 | 210 | ret = f3kdb_params_fill_preset(¶ms, "high/chroma"); 211 | ASSERT_EQ(F3KDB_SUCCESS, ret); 212 | ASSERT_EQ(0xDEADBEEF, params.seed); 213 | ASSERT_EQ(0, params.Y); 214 | ASSERT_NE(0, params.Cb); 215 | ASSERT_NE(0, params.Cr); 216 | ASSERT_EQ(0, params.grainY); 217 | ASSERT_NE(0, params.grainC); 218 | } -------------------------------------------------------------------------------- /test/wscript: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from waflib import Utils, Build 4 | from waflib.TaskGen import feature, before_method, after_method 5 | from waflib.Task import compile_fun 6 | 7 | 8 | def options(opt): 9 | opt.load("compiler_c") 10 | opt.add_option("--enable-test", action="store_true", default=False, 11 | dest="test", help="build tests") 12 | opt.add_option("--disable-test", action="store_false", 13 | dest="test", help="do not build tests (default)") 14 | 15 | 16 | def configure(conf): 17 | conf.msg_feature("Tests", conf.options.test) 18 | if not conf.options.test: 19 | return 20 | 21 | conf.env.BUILD_TEST = True 22 | conf.load("compiler_c") 23 | conf.find_program("cmake", var="CMAKE") 24 | conf.find_program("make", var="MAKE") 25 | 26 | if conf.env.STATIC and conf.env.DEST_OS_NORMALIZED == "windows": 27 | lib_param_name = "stlib" 28 | else: 29 | lib_param_name = "lib" 30 | 31 | if not any(conf.check_cxx( 32 | uselib_store="PTHREAD", 33 | mandatory=False, 34 | **{lib_param_name: x} 35 | ) 36 | for x in ["pthreads", "pthread"]): 37 | conf.fatal("Can't find pthread.") 38 | 39 | googletest_dir = Utils.subst_vars("${VENDORLIBS}/googletest", conf.env) 40 | if not os.path.isfile(os.path.join(googletest_dir, "CMakeLists.txt")): 41 | conf.fatal("Can't find Google Test, please run 'git submodule update --init --recursive' to update git submodules") 42 | 43 | conf.env.GTEST_ROOT = googletest_dir 44 | 45 | 46 | @feature('static') 47 | @after_method('apply_link') 48 | def make_fully_static(self): 49 | assert self.link_task 50 | self.link_task.hcode += " -Wl,-Bstatic" 51 | func, _ = compile_fun(self.link_task.hcode) 52 | self.link_task.run = lambda: func(self.link_task) 53 | 54 | @feature('static') 55 | @before_method('apply_link') 56 | def make_fully_static_before(self): 57 | self.env.append_unique("LINKFLAGS", ["-static"]) 58 | 59 | 60 | def build(bld): 61 | if not bld.env.BUILD_TEST: 62 | return 63 | 64 | bld.post_mode = Build.POST_LAZY 65 | 66 | bld.add_group() 67 | gtest_build = bld.path.get_bld().make_node("gtest") 68 | gtest_build.mkdir() 69 | 70 | makefile = gtest_build.make_node("Makefile") 71 | cmakelists = bld.root.find_node(bld.env.GTEST_ROOT + "/CMakeLists.txt") 72 | bld( 73 | rule="CXX=${CXX} CC=${CC} ${CMAKE} ${GTEST_ROOT}", 74 | source=cmakelists, 75 | target=makefile, 76 | cwd=gtest_build.abspath(), 77 | ) 78 | gtest_output = [gtest_build.make_node(x) 79 | for x in ("libgtest.a", "libgtest_main.a")] 80 | bld( 81 | name="gtest", 82 | rule="${MAKE}", 83 | source=makefile, 84 | target=gtest_output, 85 | cwd=gtest_build.abspath(), 86 | ) 87 | bld( 88 | rule="${PYTHON3} ${SRC[0].abspath()} > ${TGT[0].abspath()}", 89 | source="build_core_param_set.py", 90 | target=bld.path.find_node("test_core_param_set.h"), 91 | cwd=bld.path.abspath(), 92 | ) 93 | bld.add_group() 94 | test_output = bld.path.get_bld().make_node("f3kdb-test") 95 | bld( 96 | features="cxx cxxprogram %s" % (( 97 | "static" if 98 | bld.env.STATIC and bld.env.DEST_OS_NORMALIZED == "windows" 99 | else "" 100 | ),), 101 | source=bld.path.ant_glob( 102 | ["*.cpp"], 103 | excl=[ 104 | "stdafx.cpp", 105 | ], 106 | ), 107 | includes=[Utils.subst_vars("${GTEST_ROOT}/include", bld.env)], 108 | use=["f3kdb", "gtest"], 109 | uselib=["PTHREAD"], 110 | stlib=["gtest", "gtest_main"], 111 | stlibpath=[gtest_build.abspath()], 112 | linkflags="-Wl,-rpath={}:.:..".format( 113 | bld.path.get_bld().parent.abspath() 114 | ), 115 | target=test_output, 116 | ) 117 | [bld.add_manual_dependency(test_output, x) for x in gtest_output] 118 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAPikachu/flash3kyuu_deband/54ff2b0c244bd3a7ecd9aa25d845f5cd739b4e20/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from waflib import Utils, Logs 4 | 5 | APPNAME = "f3kdb" 6 | VERSION = "2.0pre" 7 | 8 | top = "." 9 | 10 | 11 | def options(opt): 12 | opt.load("compiler_cxx") 13 | 14 | conf_opt = opt.get_option_group("configure options") 15 | 16 | conf_opt.add_option("--libdir", action="store", default="${PREFIX}/lib", 17 | help="library installation directory") 18 | conf_opt.add_option("--includedir", action="store", 19 | default="${PREFIX}/include/f3kdb", 20 | help="header installation directory") 21 | 22 | conf_opt.add_option("--mode", action="store", default="release", 23 | help="the mode to compile in (debug/release)") 24 | conf_opt.add_option("--shared", action="store_true", default=True, 25 | help="build shared libraries (default)") 26 | conf_opt.add_option("--no-shared", action="store_false", dest="shared", 27 | help="do not build shared libraries") 28 | conf_opt.add_option("--static", action="store_true", default=False, 29 | help="build static libraries") 30 | conf_opt.add_option("--no-static", action="store_false", dest="static", 31 | help="do not build static libraries (default)") 32 | 33 | conf_opt.add_option("--enable-vs", "--enable-vapoursynth", 34 | action="store_true", dest="vapoursynth", default=True, 35 | help="enable Vapoursynth support (default)") 36 | conf_opt.add_option("--disable-vs", "--disable-vapoursynth", 37 | action="store_false", dest="vapoursynth", 38 | help="disable Vapoursynth support") 39 | 40 | conf_opt.add_option("--enable-avs", "--enable-avisynth", 41 | action="store_true", dest="avisynth", default=True, 42 | help="enable Avisynth support (default, Windows only)") 43 | conf_opt.add_option("--disable-avs", "--disable-avisynth", 44 | action="store_false", dest="avisynth", 45 | help="disable Avisynth support") 46 | 47 | inst_opt = opt.get_option_group("install/uninstall options") 48 | inst_opt.add_option("--no-ldconfig", action="store_false", 49 | dest="ldconfig", default=True, 50 | help="don't run ldconfig after install " 51 | "(default: run, non-Windows only)") 52 | 53 | opt.recurse("test") 54 | 55 | 56 | def _check_cxx(conf, feature, fragment, mandatory=False): 57 | conf.check_cxx( 58 | msg=" - " + feature, 59 | define_name="HAVE_" + feature.replace(" ", "_").upper(), 60 | fragment=fragment, 61 | mandatory=mandatory, 62 | ) 63 | 64 | def _check_optional(conf, **kwargs): 65 | args = {'mandatory': False, 'uselib_store': 'OPTIONAL'} 66 | args.update(kwargs) 67 | return conf.check_cxx(**args) 68 | 69 | def configure_gcc(conf): 70 | # clang is also configured here, since their configurations are the same 71 | def add_options(flags, options): 72 | for flag in flags: 73 | conf.env.append_unique(flag, options) 74 | 75 | add_options(["CFLAGS", "CXXFLAGS"], 76 | ["-Wall", "-Wextra", "-Wno-unused-parameter", 77 | "-fvisibility=hidden", "-fvisibility-inlines-hidden", 78 | "-Werror",]) 79 | 80 | if conf.env.DEST_OS == "cygwin": 81 | add_options(["CFLAGS", "CXXFLAGS"], ["-std=gnu++11"]) 82 | else: 83 | add_options(["CFLAGS", "CXXFLAGS"], ["-std=c++11"]) 84 | 85 | _check_optional(conf, cflags='-fPIC', cxxflags='-fPIC') 86 | _check_optional(conf, linkflags='-fPIC') 87 | 88 | _check_optional(conf, linkflags='-Wl,-Bsymbolic') 89 | _check_optional(conf, linkflags='-Wl,-z,noexecstack') 90 | 91 | add_options(["CFLAGS"], conf.env.CFLAGS_OPTIONAL) 92 | add_options(["CXXFLAGS"], conf.env.CXXFLAGS_OPTIONAL) 93 | add_options(["LINKFLAGS_cshlib", 94 | "LINKFLAGS_cprogram", 95 | "LINKFLAGS_cxxshlib", 96 | "LINKFLAGS_cxxprogram"], 97 | conf.env.LINKFLAGS_OPTIONAL) 98 | 99 | if conf.options.mode == "debug": 100 | add_options(["CFLAGS", "CXXFLAGS"], 101 | ["-g", "-ggdb", "-ftrapv"]) 102 | elif conf.options.mode == "release": 103 | add_options(["CFLAGS", "CXXFLAGS"], 104 | ["-O3"]) 105 | else: 106 | conf.fatal("--mode must be either debug or release.") 107 | 108 | _check_cxx( 109 | conf, 110 | "alignas", 111 | "int main() { alignas(8) int x = 0; return x; }", 112 | mandatory=True, 113 | ) 114 | 115 | for dir in ["libdir", "includedir"]: 116 | u = dir.upper() 117 | 118 | conf.env[u] = Utils.subst_vars(conf.options.__dict__[dir], conf.env) 119 | conf.msg("Setting {0} to".format(u), conf.env[u]) 120 | 121 | conf.env.VENDORLIBS = conf.path.find_node("lib").abspath() 122 | conf.env.append_value("INCLUDES", conf.path.find_node("include").abspath()) 123 | 124 | conf.find_program("python3", var="PYTHON3") 125 | 126 | 127 | def configure(conf): 128 | conf.msg_feature = lambda feature, v: conf.msg(feature, "yes" if v else "no") 129 | for x in ["shared", "static"]: 130 | val = conf.options.__dict__[x] 131 | conf.env[x.upper()] = val 132 | conf.msg_feature(x.title() + " library", val) 133 | 134 | if (conf.env.SHARED, conf.env.STATIC) == (False, False): 135 | conf.fatal("Either shared or static library need to be selected.") 136 | 137 | if "MSVC_VERSIONS" not in conf.env: 138 | conf.env["MSVC_VERSIONS"] = ["msvc 11.0"] 139 | 140 | conf.load("compiler_cxx") 141 | dest_os = ( 142 | "windows" if conf.env.DEST_OS in ["win32", "cygwin", "msys", "uwin"] 143 | else conf.env.DEST_OS 144 | ) 145 | conf.env.DEST_OS_NORMALIZED = dest_os 146 | 147 | conf.env.USE_MSBUILD = dest_os == "windows" and conf.env.CXX_NAME != "gcc" 148 | if conf.env.USE_MSBUILD: 149 | conf.recurse("msvc") 150 | else: 151 | configure_gcc(conf) 152 | 153 | conf.env.ENABLE_VAPOURSYNTH = conf.options.vapoursynth 154 | conf.msg_feature("Vapoursynth support", conf.env.ENABLE_VAPOURSYNTH) 155 | 156 | if not conf.env.USE_MSBUILD: 157 | conf.recurse("test") 158 | 159 | conf.msg("Build mode", conf.options.mode) 160 | 161 | conf.env.APPNAME = APPNAME 162 | 163 | 164 | def post_install(ctx): 165 | if not ctx.options.ldconfig: 166 | return 167 | 168 | if not os.path.isfile("/sbin/ldconfig"): 169 | return 170 | 171 | Logs.info("- ldconfig") 172 | ctx.exec_command(Utils.subst_vars("/sbin/ldconfig ${LIBDIR}", ctx.env)) 173 | 174 | 175 | def build(bld): 176 | if bld.env.USE_MSBUILD: 177 | bld.recurse("msvc") 178 | return 179 | 180 | bld.recurse("src") 181 | for var, feature in [("SHARED", "cxxshlib"), ("STATIC", "cxxstlib")]: 182 | if bld.env[var]: 183 | bld(features="cxx " + feature, 184 | use=[ 185 | "f3kdb-objs", 186 | "f3kdb-impl-sse2", 187 | "f3kdb-impl-ssse3", 188 | "f3kdb-impl-sse4", 189 | ], 190 | target="f3kdb", 191 | install_path="${LIBDIR}") 192 | 193 | bld.install_files("${INCLUDEDIR}", bld.path.ant_glob(["include/*.h"])) 194 | 195 | if bld.cmd == "install": 196 | bld.add_post_fun(post_install) 197 | 198 | bld.recurse("test") 199 | --------------------------------------------------------------------------------