diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a703d7b..4fbebd0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ release: ${{ steps.tag_release.outputs.release }} version: ${{ steps.tag_release.outputs.version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v3 with: @@ -51,7 +51,7 @@ needs: [semantic-release] if: ${{ needs.semantic-release.outputs.release == 'True' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: apt install deps run: | sudo apt-get update -y -q @@ -62,22 +62,22 @@ cmake -G "Ninja" ../ ninja - name: Upload linux filter - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-linux-filter path: build/src/filter/**/*.so - name: Upload linux mixer2 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-linux-mixer2 path: build/src/mixer2/**/*.so - name: Upload linux mixer3 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-linux-mixer3 path: build/src/mixer3/**/*.so - name: Upload linux generator - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-linux-generator path: build/src/generator/**/*.so @@ -88,7 +88,7 @@ needs: [semantic-release] if: ${{ needs.semantic-release.outputs.release == 'True' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ilammy/msvc-dev-cmd@v1 - name: choco install deps uses: crazy-max/ghaction-chocolatey@v2 @@ -100,22 +100,22 @@ cmake -G "NMake Makefiles" ../ nmake - name: Upload win64 filter - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-win64-filter path: build/src/filter/**/*.dll - name: Upload win64 mixer2 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-win64-mixer2 path: build/src/mixer2/**/*.dll - name: Upload win64 mixer3 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-win64-mixer3 path: build/src/mixer3/**/*.dll - name: Upload win64 generator - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-win64-generator path: build/src/generator/**/*.dll @@ -126,36 +126,36 @@ needs: [semantic-release] if: ${{ needs.semantic-release.outputs.release == 'True' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Update Homebrew run: | - brew update --preinstall + brew update - name: Install Homebrew dependencies run: | env HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1 \ - brew install cmake ninja cairo + brew install ninja cairo - name: Build using ninja run: | mkdir build && cd build cmake -G "Ninja" ../ ninja - name: Upload osx filter - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-osx-filter path: build/src/filter/**/*.so - name: Upload osx mixer2 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-osx-mixer2 path: build/src/mixer2/**/*.so - name: Upload osx mixer3 - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-osx-mixer3 path: build/src/mixer3/**/*.so - name: Upload osx generator - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-osx-generator path: build/src/generator/**/*.so @@ -166,9 +166,9 @@ if: ${{ needs.semantic-release.outputs.release == 'True' }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: download binary artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: | frei0r-bin @@ -183,7 +183,7 @@ cp README.md $dst/README.txt cp COPYING $dst/LICENSE.txt cp ChangeLog $dst/ChangeLog.txt - cp AUTHORS $dst/AUTHORS.txt + cp AUTHORS.md $dst/AUTHORS.txt cp include/frei0r.h include/frei0r.hpp $dst/ echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt zip -r -9 $dst.zip $dst @@ -197,7 +197,7 @@ cp README.md $dst/README.txt cp COPYING $dst/LICENSE.txt cp ChangeLog $dst/ChangeLog.txt - cp AUTHORS $dst/AUTHORS.txt + cp AUTHORS.md $dst/AUTHORS.txt cp include/frei0r.h include/frei0r.hpp $dst/ echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt zip -r -9 $dst.zip $dst @@ -211,7 +211,7 @@ cp README.md $dst/README.txt cp COPYING $dst/LICENSE.txt cp ChangeLog $dst/ChangeLog.txt - cp AUTHORS $dst/AUTHORS.txt + cp AUTHORS.md $dst/AUTHORS.txt cp include/frei0r.h include/frei0r.hpp $dst/ echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt tar cvfz $dst.tar.gz $dst diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7c12a9c..8943f4c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ # name: 🚨 REUSE Compliance # runs-on: ubuntu-latest # steps: - # - uses: actions/checkout@v3 + # - uses: actions/checkout@v4 # - uses: fsfe/reuse-action@v1 c-lint: @@ -35,7 +35,7 @@ runs-on: ubuntu-latest if: "!contains(github.event.pull_request.labels.*.name, 'skip-lint')" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: reviewdog/action-cpplint@master env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -62,6 +62,7 @@ ,-whitespace/semicolon\ ,-build/include_subdir\ ,-build/include_order\ + ,-build/header_guard\ " test-suite: @@ -74,7 +75,7 @@ fail-fast: false runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: install dependencies run: | sudo apt-get update -qy @@ -90,8 +91,7 @@ run: | cd test && make - name: ${{ matrix.compiler }} upload plugin analysis - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-plugin-analysis path: test/*.json - diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 802e49f..0000000 --- a/AUTHORS +++ /dev/null @@ -1,51 +0,0 @@ -# Frei0r developers union - -Read here an account of [Frei0r's project history](https://medium.com/think-do-tank/frei0r-the-free-and-open-source-video-effect-preservation-project-604134dde8b3). - -Frei0r is maintained by [Jaromil](https://jaromil.dyne.org) for the Dyne.org Foundation. - -## Developers who contributed, in alphabetic order: - -Akito Iwakura -Albert Frisch -Brendan Hack -Brian Matherly -Burkhard Plaum -Carlo E. Prelz -Christoph Willing -Dan Dennedy -Filippo Giunchedi -Gabriel Finch (Salsaman) -Georg Seidel -Henner Zeller -Hedde Bosman -IOhannes M. Zmölnig -Janne Liljeblad -Jean-Baptiste Mardelle -Jean-François Fortin Tam -Jean-Sebastien Senecal -Jerome Blanchi (d.j.a.y) -Joshua M. Doe -Luca Bigliardi -Maksim Golovkin (Максим Головкин) -Marko Cebokli -Martin Bayer -Mathieu Guindon -Matthias Schnoell -Nicolas Carion -Niels Elburg -Phillip Promesberger -Raphael Graf -Richard Spindler -Richard Ling (Chungzuwalla) -Robert Schweikert -Ross Lagerwall -Samuel Mimram -Simon A. Eugster -Sofian Audry -Stefano Sabatini -Steinar H. Gunderson -Thomas Coldrick -Thomas Perl -Till Theato -Vincent Pinon diff --git a/AUTHORS.md b/AUTHORS.md index 5fd9481..650e026 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,50 +1,9 @@ +# Frei0r developers union -Project initiated at the Piksel Festival in 2004 -hosted by BEK - Bergen Senter for Elektronisk Kunst -Maintained at the Dyne.org Foundation +Read here an account of [Frei0r's project history](https://medium.com/think-do-tank/frei0r-the-free-and-open-source-video-effect-preservation-project-604134dde8b3). -Developers who contributed, in alphabetic order: +Frei0r is a Dyne.org project maintained by Denis "Jaromil" Roio and Dan Dennedy. -Akito Iwakura -Albert Frisch -Brian Matherly -Burkhard Plaum -Carlo E. Prelz -Christoph Willing -Dan Dennedy -Denis Roio (Jaromil) -Filippo Giunchedi -Gabriel Finch (Salsaman) -Georg Seidel -Henner Zeller -Hedde Bosman -IOhannes M. Zmölnig -Janne Liljeblad -Jean-Baptiste Mardelle -Jean-François Fortin Tam -Jean-Sebastien Senecal -Jerome Blanchi (d.j.a.y) -Joshua M. Doe -Luca Bigliardi -Maksim Golovkin (Максим Головкин) -Marko Cebokli -Martin Bayer -Mathieu Guindon -Matthias Schnoell -Nicolas Carion -Niels Elburg -Phillip Promesberger -Raphael Graf -Richard Spindler -Richard Ling (Chungzuwalla) -Robert Schweikert -Ross Lagerwall -Samuel Mimram -Simon A. Eugster -Sofian Audry -Stefano Sabatini -Steinar H. Gunderson -Thomas Coldrick -Thomas Perl -Till Theato -Vincent Pinon +## Developers who contributed, in alphabetic order: + +Akito Iwakura, Albert Frisch, Ajrat Makhmutov, Brendan Hack, Brian Matherly, Burkhard Plaum, Carlo E. Prelz, Christoph Willing, Erik Beck, Esmane, Filippo Giunchedi, Gabriel Finch (Salsaman), Georg Seidel, Henner Zeller, Hedde Bosman, IOhannes M. Zmölnig, Janne Liljeblad, Jean-Baptiste Mardelle, Jean-François Fortin Tam , Jean-Sebastien Senecal, Jerome Blanchi (d.j.a.y), Joshua M. Doe, Luca Bigliardi, Maksim Golovkin (Максим Головкин), Marko Cebokli, Martin Bayer, Mathieu Guindon, Matthias Schnoell, Nicolas Carion, Niels Elburg, Phillip Promesberger, Raphael Graf, Richard Spindler, Richard Ling (Chungzuwalla), Robert Schweikert, Ross Lagerwall, Samuel Mimram, Simon A. Eugster, Sofian Audry, Stefano Sabatini, Steinar H. Gunderson, Thomas Coldrick, Thomas Perl, Till Theato, Vincent Pinon. diff --git a/CMakeLists.txt b/CMakeLists.txt index 96d4c85..4e25222 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.1) +cmake_minimum_required (VERSION 3.12...3.31) list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) @@ -35,8 +35,7 @@ # --- custom targets: --- INCLUDE( cmake/modules/TargetDistclean.cmake OPTIONAL) -# See this thread for a ridiculous discussion about the simple question how to install a header file with CMake: http://www.cmake.org/pipermail/cmake/2009-October/032874.html -install (DIRECTORY include DESTINATION . FILES_MATCHING PATTERN "frei0r.h" PATTERN "msvc" EXCLUDE) +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # For code documentation run: doxygen doc/Doxyfile # add_subdirectory (doc) diff --git a/cmake/modules/TargetDistclean.cmake b/cmake/modules/TargetDistclean.cmake index 5abbb35..a1be724 100644 --- a/cmake/modules/TargetDistclean.cmake +++ b/cmake/modules/TargetDistclean.cmake @@ -3,9 +3,11 @@ # Jan Woetzel 04/2003 # -IF (UNIX) - ADD_CUSTOM_TARGET (distclean @echo cleaning for source distribution) - SET(DISTCLEANED +if(UNIX) + add_custom_target(distclean + COMMENT "cleaning for source distribution" + ) + set(DISTCLEANED cmake.depends cmake.check_depends CMakeCache.txt @@ -16,13 +18,18 @@ gmon.out *~ ) - - ADD_CUSTOM_COMMAND( - DEPENDS clean + add_custom_command( + POST_BUILD + COMMENT "running target clean" + TARGET distclean + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean + ) + add_custom_command( + POST_BUILD COMMENT "distribution clean" COMMAND rm ARGS -Rf CMakeTmp ${DISTCLEANED} TARGET distclean ) -ENDIF(UNIX) +endif(UNIX) diff --git a/include/blur.h b/include/blur.h deleted file mode 100644 index 8af120d..0000000 --- a/include/blur.h +++ /dev/null @@ -1,270 +0,0 @@ -/* blur.h - * Copyright (C) 2004--2005 Mathieu Guindon - * Julien Keable - * Jean-Sebastien Senecal (js@drone.ws) - * - * Modified by Richard Spindler (richard.spindler AT gmail.com) for blurring in - * the mask0mate Frei0r plugin. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include - -#include "frei0r.h" - -#define SIZE_RGBA 4 - -static inline int MAX(int a, int b) -{ - return (a > b ? a : b); -} - -static inline int MIN(int a, int b) -{ - return (a < b ? a : b); -} - -static inline void subtract_acc(uint32_t *dst, const uint32_t *src) -{ - int n=SIZE_RGBA; - while (n--) - *dst++ -= *src++; -} - -static inline void add_acc(uint32_t *dst, const uint32_t *src) -{ - int n=SIZE_RGBA; - while (n--) - *dst++ += *src++; -} - -static inline void divide(unsigned char *dst, const uint32_t *src, const unsigned int val) -{ - int n=SIZE_RGBA; - while (n--) - *dst++ = *src++ / val; -} - -typedef struct squareblur_instance -{ - unsigned int width; - unsigned int height; - double kernel; /* the kernel size, as a percentage of the biggest of width and height */ - uint32_t *mem; /* memory accumulation matrix of uint32_t (size = acc_width*acc_height*SIZE_RGBA) */ - uint32_t **acc; /* accumulation matrix of pointers to SIZE_RGBA consecutive uint32_t in mem (size = acc_width*acc_height) */ -} squareblur_instance_t; - -/* Updates the summed area table. */ -static void update_summed_area_table(squareblur_instance_t *inst, const uint32_t *src) -{ - register unsigned char *iter_data; - register uint32_t *iter_mem; - register unsigned int i, x, y; - - uint32_t acc_buffer[SIZE_RGBA]; /* accumulation buffer */ - - unsigned int row_width; - unsigned int width, height; - unsigned int cell_size; - - /* Compute basic params. */ - width = inst->width+1; - height = inst->height+1; - row_width = SIZE_RGBA * width; - cell_size = SIZE_RGBA * sizeof(uint32_t); - - /* Init iterators. */ - iter_data = (unsigned char*) src; - iter_mem = inst->mem; - - /* Process first row (all zeros). */ - memset(iter_mem, 0, row_width * cell_size); - iter_mem += row_width; - - if (height >= 1) - { - /* Process second row. */ - memset(acc_buffer, 0, cell_size); - memset(iter_mem, 0, cell_size); /* first column is void */ - iter_mem += SIZE_RGBA; - for (x=1; xname = "Kernel size"; - info->type = F0R_PARAM_DOUBLE; - info->explanation = "The size of the kernel, as a proportion to its coverage of the image"; - break; - } -} - -static f0r_instance_t blur_construct(unsigned int width, unsigned int height) -{ - squareblur_instance_t* inst = - (squareblur_instance_t*)malloc(sizeof(squareblur_instance_t)); - unsigned int i; - unsigned int acc_width, acc_height = height+1; - uint32_t* iter_mem; - uint32_t** iter_acc; - /* set params */ - inst->width = width; inst->height = height; - acc_width = width+1; acc_height = height+1; - inst->kernel = 0.0; - /* allocate memory for the summed-area-table */ - inst->mem = (uint32_t*) malloc(acc_width*acc_height*SIZE_RGBA*sizeof(uint32_t)); - inst->acc = (uint32_t**) malloc(acc_width*acc_height*sizeof(uint32_t*)); - /* point at the right place */ - iter_mem = inst->mem; - iter_acc = inst->acc; - for (i=0; iacc); - free(inst->mem); - free(instance); -} - -static void blur_set_param_value(f0r_instance_t instance, - f0r_param_t param, int param_index) -{ - assert(instance); - squareblur_instance_t* inst = (squareblur_instance_t*)instance; - - switch(param_index) - { - case 0: - /* kernel size */ - inst->kernel = *((double*)param); - break; - } -} - -static void blur_get_param_value(f0r_instance_t instance, - f0r_param_t param, int param_index) -{ - assert(instance); - squareblur_instance_t* inst = (squareblur_instance_t*)instance; - - switch(param_index) - { - case 0: - *((double*)param) = inst->kernel; - break; - } -} - -static void blur_update(f0r_instance_t instance, double time, - const uint32_t* inframe, uint32_t* outframe) -{ - assert(instance); - squareblur_instance_t* inst = (squareblur_instance_t*)instance; - - unsigned int width = inst->width; - unsigned int height = inst->height; - unsigned int acc_width = width+1; /* width of the summed area table */ - unsigned int max = MAX(width, height); - unsigned int kernel_size = (unsigned int) (inst->kernel * max / 2.0); - - unsigned int x, y; - unsigned int x0, x1, y0, y1; - unsigned int area; - - if (kernel_size <= 0) - { - /* No blur, just copy image. */ - memcpy(outframe, inframe, width*height*sizeof(uint32_t)); - } - else - { - assert(inst->acc); - unsigned char* dst = (unsigned char*)outframe; - uint32_t** acc = inst->acc; - uint32_t sum[SIZE_RGBA]; - unsigned int y0_offset, y1_offset; - - /* Compute the summed area table. */ - update_summed_area_table(inst, inframe); - - /* Loop through the image's pixels. */ - for (y=0;y +#include +#include + +#include "frei0r.h" + +#define SIZE_RGBA 4 + +static inline int MAX(int a, int b) +{ + return (a > b ? a : b); +} + +static inline int MIN(int a, int b) +{ + return (a < b ? a : b); +} + +static inline void subtract_acc(uint32_t *dst, const uint32_t *src) +{ + int n=SIZE_RGBA; + while (n--) + *dst++ -= *src++; +} + +static inline void add_acc(uint32_t *dst, const uint32_t *src) +{ + int n=SIZE_RGBA; + while (n--) + *dst++ += *src++; +} + +static inline void divide(unsigned char *dst, const uint32_t *src, const unsigned int val) +{ + int n=SIZE_RGBA; + while (n--) + *dst++ = *src++ / val; +} + +typedef struct squareblur_instance +{ + unsigned int width; + unsigned int height; + double kernel; /* the kernel size, as a percentage of the biggest of width and height */ + uint32_t *mem; /* memory accumulation matrix of uint32_t (size = acc_width*acc_height*SIZE_RGBA) */ + uint32_t **acc; /* accumulation matrix of pointers to SIZE_RGBA consecutive uint32_t in mem (size = acc_width*acc_height) */ +} squareblur_instance_t; + +/* Updates the summed area table. */ +static void update_summed_area_table(squareblur_instance_t *inst, const uint32_t *src) +{ + register unsigned char *iter_data; + register uint32_t *iter_mem; + register unsigned int i, x, y; + + uint32_t acc_buffer[SIZE_RGBA]; /* accumulation buffer */ + + unsigned int row_width; + unsigned int width, height; + unsigned int cell_size; + + /* Compute basic params. */ + width = inst->width+1; + height = inst->height+1; + row_width = SIZE_RGBA * width; + cell_size = SIZE_RGBA * sizeof(uint32_t); + + /* Init iterators. */ + iter_data = (unsigned char*) src; + iter_mem = inst->mem; + + /* Process first row (all zeros). */ + memset(iter_mem, 0, row_width * cell_size); + iter_mem += row_width; + + if (height >= 1) + { + /* Process second row. */ + memset(acc_buffer, 0, cell_size); + memset(iter_mem, 0, cell_size); /* first column is void */ + iter_mem += SIZE_RGBA; + for (x=1; xname = "Kernel size"; + info->type = F0R_PARAM_DOUBLE; + info->explanation = "The size of the kernel, as a proportion to its coverage of the image"; + break; + } +} + +static f0r_instance_t blur_construct(unsigned int width, unsigned int height) +{ + squareblur_instance_t* inst = + (squareblur_instance_t*)malloc(sizeof(squareblur_instance_t)); + unsigned int i; + unsigned int acc_width, acc_height = height+1; + uint32_t* iter_mem; + uint32_t** iter_acc; + /* set params */ + inst->width = width; inst->height = height; + acc_width = width+1; acc_height = height+1; + inst->kernel = 0.0; + /* allocate memory for the summed-area-table */ + inst->mem = (uint32_t*) malloc(acc_width*acc_height*SIZE_RGBA*sizeof(uint32_t)); + inst->acc = (uint32_t**) malloc(acc_width*acc_height*sizeof(uint32_t*)); + /* point at the right place */ + iter_mem = inst->mem; + iter_acc = inst->acc; + for (i=0; iacc); + free(inst->mem); + free(instance); +} + +static void blur_set_param_value(f0r_instance_t instance, + f0r_param_t param, int param_index) +{ + assert(instance); + squareblur_instance_t* inst = (squareblur_instance_t*)instance; + + switch(param_index) + { + case 0: + /* kernel size */ + inst->kernel = *((double*)param); + break; + } +} + +static void blur_get_param_value(f0r_instance_t instance, + f0r_param_t param, int param_index) +{ + assert(instance); + squareblur_instance_t* inst = (squareblur_instance_t*)instance; + + switch(param_index) + { + case 0: + *((double*)param) = inst->kernel; + break; + } +} + +static void blur_update(f0r_instance_t instance, double time, + const uint32_t* inframe, uint32_t* outframe) +{ + assert(instance); + squareblur_instance_t* inst = (squareblur_instance_t*)instance; + + unsigned int width = inst->width; + unsigned int height = inst->height; + unsigned int acc_width = width+1; /* width of the summed area table */ + unsigned int max = MAX(width, height); + unsigned int kernel_size = (unsigned int) (inst->kernel * max / 2.0); + + unsigned int x, y; + unsigned int x0, x1, y0, y1; + unsigned int area; + + if (kernel_size <= 0) + { + /* No blur, just copy image. */ + memcpy(outframe, inframe, width*height*sizeof(uint32_t)); + } + else + { + assert(inst->acc); + unsigned char* dst = (unsigned char*)outframe; + uint32_t** acc = inst->acc; + uint32_t sum[SIZE_RGBA]; + unsigned int y0_offset, y1_offset; + + /* Compute the summed area table. */ + update_summed_area_table(inst, inframe); + + /* Loop through the image's pixels. */ + for (y=0;y +#include +#include "frei0r/math.h" + +/** +* String identifiers for gradient types available using Cairo. +*/ +#define GRADIENT_LINEAR "gradient_linear" +#define GRADIENT_RADIAL "gradient_radial" + +/** +* String identifiers for blend modes available using Cairo. +*/ +#define NORMAL "normal" +#define ADD "add" +#define SATURATE "saturate" +#define MULTIPLY "multiply" +#define SCREEN "screen" +#define OVERLAY "overlay" +#define DARKEN "darken" +#define LIGHTEN "lighten" +#define COLORDODGE "colordodge" +#define COLORBURN "colorburn" +#define HARDLIGHT "hardlight" +#define SOFTLIGHT "softlight" +#define DIFFERENCE "difference" +#define EXCLUSION "exclusion" +#define HSLHUE "hslhue" +#define HSLSATURATION "hslsaturation" +#define HSLCOLOR "hslcolor" +#define HSLLUMINOSITY "hslluminosity" + + +/** +* frei0r_cairo_set_operator +* @cr: Cairo context +* @op: String identifier for a blend mode +* +* Sets cairo context to use the defined blend mode for all paint operations. +*/ +void frei0r_cairo_set_operator(cairo_t *cr, char *op) +{ + if(strcmp(op, NORMAL) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + } + else if(strcmp(op, ADD) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_ADD); + } + else if(strcmp(op, SATURATE) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); + } + else if(strcmp(op, MULTIPLY) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY); + } + else if(strcmp(op, SCREEN) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_SCREEN); + } + else if(strcmp(op, OVERLAY) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY); + } + else if(strcmp(op, DARKEN) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN); + } + else if(strcmp(op, LIGHTEN) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_LIGHTEN); + } + else if(strcmp(op, COLORDODGE) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE); + } + else if(strcmp(op, COLORBURN) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_BURN); + } + else if(strcmp(op, HARDLIGHT) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_HARD_LIGHT); + } + else if(strcmp(op, SOFTLIGHT) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT); + } + else if(strcmp(op, DIFFERENCE) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE); + } + else if(strcmp(op, EXCLUSION) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_EXCLUSION); + } + else if(strcmp(op, HSLHUE) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_HSL_HUE); + } + else if(strcmp(op, HSLSATURATION) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION); + } + else if(strcmp(op, HSLCOLOR) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_HSL_COLOR); + } + else if(strcmp(op, HSLLUMINOSITY ) == 0) + { + cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY); + } + else + { + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + } +} + + +/** +* frei0r_cairo_set_rgba_LITTLE_ENDIAN +* @cr: Cairo context +* @red: red component, 0 - 1 +* @green: green component, 0 - 1 +* @blue: blue component, 0 - 1 +* @alpha: opacity of color, 0 -1 +* +* Sets cairo context to use the defined color paint operations. +* Switches red and blue channels to get correct color on little endian machines. +* This method only works correctly on little endian machines. +*/ +void frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue, double alpha) +{ + cairo_set_source_rgba (cr, blue, green, red, alpha); +} + +/** +* frei0r_cairo_set_rgb_LITTLE_ENDIAN +* @cr: Cairo context +* @red: red component, 0 - 1 +* @green: green component, 0 - 1 +* @blue: blue component, 0 - 1 +* +* Sets cairo context to use the defined color paint operations. +* Switches red and blue channels to get correct color on little endian machines. +* This method only works correctly on little endian machines. +*/ +void frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue) +{ + cairo_set_source_rgb (cr, blue, green, red); +} + +/** +* freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN( +* @pat: Cairo pattern +* @offset: offset of color position in pattern space, 0 - 1 +* @red: red component, 0 - 1 +* @green: green component, 0 - 1 +* @blue: blue component, 0 - 1 +* @alpha: opacity of color, 0 -1 +* +* Sets color stop for cairo patterns. +* Switches red and blue channels to get correct color on little endian machines. +* This method only works correctly on little endian machines. +*/ +void freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t *pat, double offset, + double red, double green, double blue, double alpha) +{ + cairo_pattern_add_color_stop_rgba (pat, offset, blue, green, red, alpha); +} + +/** +* frei0r_cairo_get_pixel_position +* @norm_pos: position in range 0 - 1, either x or y +* @dim: dimension, either witdh or height +* +* Converts double range [0 -> 1] to pixel range [-2*dim -> 3*dim]. Input 0.4 gives position 0. +* +* Returns: position in pixels +*/ +double frei0r_cairo_get_pixel_position (double norm_pos, int dim) +{ + double pos_o = -(dim * 2.0); + return pos_o + norm_pos * dim * 5.0; +} + +/** +* frei0r_cairo_get_scale +* @norm_scale: scale in range 0 - 1 +* +* Converts double range [0 -> 1] to scale range [0 -> 5]. Input 0.2 gives scale 1.0. +* +* Returns: scale +*/ +double frei0r_cairo_get_scale (double norm_scale) +{ + return norm_scale * 5.0; +} + +/** + * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. + * + * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 + * \param pixels the size of the image buffer in number of pixels + * \param alpha if >= 0, the alpha channel will be set to this value + * \see frei0r_cairo_unpremultiply_rgba + */ +void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) +{ + int i = pixels + 1; + while ( --i ) { + register unsigned char a = rgba[3]; + if (a == 0) { + *((uint32_t *)rgba) = 0; + } else if (a < 0xff) { + rgba[0] = ( rgba[0] * a ) >> 8; + rgba[1] = ( rgba[1] * a ) >> 8; + rgba[2] = ( rgba[2] * a ) >> 8; + } + if (alpha >= 0) rgba[3] = alpha; + rgba += 4; + } +} + +/** + * Convert Cairo ARGB pre-multiplied alpha to frei0r straight RGBA. + * + * \param rgba the image buffer with format CAIRO_FORMAT_ARGB32 + * \param pixels the size of the image buffer in number of pixels + * \see frei0r_cairo_premultiply_rgba + */ +void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) +{ + int i = pixels + 1; + while ( --i ) { + register unsigned char a = rgba[3]; + if (a > 0 && a < 0xff) { + rgba[0] = MIN(( rgba[0] << 8 ) / a, 255); + rgba[1] = MIN(( rgba[1] << 8 ) / a, 255); + rgba[2] = MIN(( rgba[2] << 8 ) / a, 255); + } + rgba += 4; + } +} + +/** + * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. + * + * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 + * \param pixels the size of the image buffer in number of pixels + * \param alpha if >= 0, the alpha channel will be set to this value + * \see frei0r_cairo_premultiply_rgba + * + * This is the same as frei0r_cairo_premultiply_rgba but it writes the + * output to a different buffer. + */ +void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, + int pixels, int alpha) +{ + int i = pixels + 1; + while ( --i ) { + register unsigned char a = in[3]; + if (a == 0) { + *((uint32_t *)out) = 0; + } else if (a == 0xff) { + memcpy(out, in, 4); + } else { + out[0] = ( in[0] * a ) >> 8; + out[1] = ( in[1] * a ) >> 8; + out[2] = ( in[2] * a ) >> 8; + if (alpha < 0) + out[3] = a; + } + if (alpha >= 0) + out[3] = alpha; + in += 4; + out += 4; + } +} diff --git a/include/frei0r/cfc.h b/include/frei0r/cfc.h new file mode 100644 index 0000000..14cca12 --- /dev/null +++ b/include/frei0r/cfc.h @@ -0,0 +1,319 @@ +/* cfc.h + * uchar->float and float->uchar conversion for packed RGBA video + * with flexible gamma correction + * + * Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu + * This file is a part of the Frei0r package + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// the float to uint8_t conversion is done +// using Spitzak type tables (upper 16 bits +// of a float value used as table index) +// see http://mysite.verizon.net/spitzak/conversion/ + +#include +#include + + +typedef struct + { + float r; + float g; + float b; + float a; + } float_rgba; + +//------------------------------------------------------ +//the following gamma functions need not be speed optimized, +//as they are only used for table generation + +//-------------------------------------------------------- +//rec 709 gamma forward (linear to gamma) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_709_f(float a) +{ +return (a<=0.018) ? 4.5*a : 1.099*powf(a,0.45)-0.099; +} + +//-------------------------------------------------------- +//rec 709 gamma backward (gamma to linear) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_709_b(float a) +{ +return (a<=0.081) ? a/4.5 : powf((a+0.099)/1.099,1.0/0.45); +} + +//---------------------------------------------------- +//sRGB gamma forward (linear to gamma) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_sRGB_f(float a) +{ +return (a<=0.0031308) ? 12.92*a : 1.055*powf(a,1.0/2.4)-0.055; +} + +//-------------------------------------------------------- +//sRGB gamma backward (gamma to linear) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_sRGB_b(float a) +{ +return (a<=0.04045) ? a/12.92 : powf((a+0.055)/1.055,2.4); +} + +//---------------------------------------------------- +//plain gamma (power function) forward (linear to gamma) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_plain_f(float a, float g) +{ +return powf(a,1.0/g); +} + +//-------------------------------------------------------- +//plain gamma (power function) backward (gamma to linear) +//a = input, should be in the 0.0 to 1.0 range +static inline float gamma_plain_b(float a, float g) +{ +return powf(a,g); +} + +//-------------------------------------------------------- +//dummy function for linear tables (no gamma) +//g = gamma value +static inline float gamma_none(float a) +{ +return a; +} + +//------------------------------------------------ +//expand highlights using a modified Spitzak formula +//with limited max output value (250) +//(for values up to 2500 use 1.0001 and 0.493) +//input [0...1] +//output [0...250] +static inline float exp_hl(float a) +{ +return (a<=0.4781) ? a : 0.25/(1.001-a); +} + +//------------------------------------------------------------ +//compress highlights using a modified Spitzak formula +//with limited max input value (250) +//(for values up to 2500 use 1.0001 and 0.493) +//input [0...250] +//output [0...1] +static inline float cps_hl(float a) +{ +return (a<=0.4781) ? a : 1.001-0.25/a; +} + +//---------------------------------------------------------- +//float to char conversion is done using the upper 16 bits +//of the float number as an index into the conversion table. +//This union is used for type punning, to avoid problems +//with compiler optimizations, as the read access in the +//cfc_tab_8 function directly follows writing +typedef union + { + float a; + uint16_t i[2]; + } flint; + +//-------------------------------------------------------- +//generate forward and backward conversion tables +//for 8 bit (uint8_t) video +//*ft = forward table (float to uchar, linear to gamma) +// must have space for 65536 char elements +//*bt = backward table (uchar to float, gamma to linear) +// must have space for 256 float elements +//type = what kind of gamma function will be used: +// 0 = linear (no gamma, just multiply/divide by 255) +// 1 = plain gamma (pure power function) +// 2 = rec 709 gamma +// 3 = sRGB gamma +// 4 = sRGB gamma with highlight expansion/compression +// types 0...3 map to [0...1] linear float range +// type 4 maps to [0...250] linear float range +//g = gamma value, 0.3 to 3.0 (only used with type=1) +static inline void cfc_tab_8(uint8_t *ft, float *bt, int type, float g) +{ +uint16_t i; +float a; +flint fi; + +// *** generate float to char conversion table *** +for (i=0;i<128;i++) //positive denormals map to zero + ft[i]=0; + +for (i=128;i<=32639;i++) //positive numbers map to 0...255 + { +//#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN +// fi.i[0]=i; fi.i[1]=0x8000; //big endian +//#endif +//#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN + fi.i[1]=i; fi.i[0]=0x8000; //little endian +//#endif + a=fi.a; + switch (type) + { + case 0: a=gamma_none(a); break; + case 1: a=gamma_plain_f(a,g); break; + case 2: a=gamma_709_f(a); break; + case 3: a=gamma_sRGB_f(a); break; + case 4: a=cps_hl(a); a=gamma_sRGB_f(a); break; + default: break; + } + if (a>0.9999) a=0.9999; + ft[i]=(uint8_t)(256.0*a); + } + +for (i=32640;i<32768;i++) //positive NANs and infinite map to 255 + ft[i]=255; +for (i=32768;i!=65535;i++) //everything negative maps to 0 + ft[i]=0; +ft[65535]=0; + + +// *** generate char to float conversion table *** +for (i=0;i<=255;i++) + { + a=((float)i+0.5)/256.0; + switch (type) + { + case 0: a=gamma_none(a); break; + case 1: a=gamma_plain_b(a,g); break; + case 2: a=gamma_709_b(a); break; + case 3: a=gamma_sRGB_b(a); break; + case 4: a=gamma_sRGB_b(a); a=exp_hl(a); break; + default: break; + } + bt[i]=a; + } +} + +//-------------------------------------------------------- +//convert from paked uchar RGBA to packed float RGBA +//w,h are width and height of the image +//tab = table used for RGB conversion +//atab = table used for alpha converion (usually linear) +static inline void RGBA8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab, float *atab) +{ +int i; +uint8_t *cin=(uint8_t *)in; + +for (i=0;ii[1]]; +} +//endif + +//#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN +//static inline float_2_uint8(const float *in, uint8_t *tab) +//{ +//return tab[((flint*)in)->i[0]]; +//} +//#endif diff --git a/include/frei0r/colorspace.h b/include/frei0r/colorspace.h new file mode 100644 index 0000000..fbccebb --- /dev/null +++ b/include/frei0r/colorspace.h @@ -0,0 +1,416 @@ +/* frei0r/colorspace.h + * Copyright (C) 2004 Mathieu Guindon, Julien Keable, Jean-Sebastien Senecal + * This file is part of Frei0r. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef INCLUDED_FREI0R_COLORSPACE_H +#define INCLUDED_FREI0R_COLORSPACE_H + +#include "frei0r/math.h" +#include +#include + +// # Basic colorspace convert functions (from the Gimp gimpcolorspace.h) #### + +/* int functions */ + +/** + * rgb_to_hsv_int + * @red: The red channel value, returns the Hue channel + * @green: The green channel value, returns the Saturation channel + * @blue: The blue channel value, returns the Value channel + * + * The arguments are pointers to int representing channel values in + * the RGB colorspace, and the values pointed to are all in the range + * [0, 255]. + * + * The function changes the arguments to point to the HSV value + * corresponding, with the returned values in the following + * ranges: H [0, 360], S [0, 255], V [0, 255]. + **/ +inline void +rgb_to_hsv_int (int *red /* returns hue */, + int *green /* returns saturation */, + int *blue /* returns value */) +{ + double r, g, b; + double h, s, v; + double min; + double delta; + + r = *red; + g = *green; + b = *blue; + + if (r > g) + { + v = MAX (r, b); + min = MIN (g, b); + } + else + { + v = MAX (g, b); + min = MIN (r, b); + } + + delta = v - min; + + if (v == 0.0) + s = 0.0; + else + s = delta / v; + + if (s == 0.0) + h = 0.0; + else + { + if (r == v) + h = 60.0 * (g - b) / delta; + else if (g == v) + h = 120 + 60.0 * (b - r) / delta; + else + h = 240 + 60.0 * (r - g) / delta; + + if (h < 0.0) + h += 360.0; + if (h > 360.0) + h -= 360.0; + } + + *red = ROUND (h); + *green = ROUND (s * 255.0); + *blue = ROUND (v); +} + +/** + * hsv_to_rgb_int + * @hue: The hue channel, returns the red channel + * @saturation: The saturation channel, returns the green channel + * @value: The value channel, returns the blue channel + * + * The arguments are pointers to int, with the values pointed to in the + * following ranges: H [0, 360], S [0, 255], V [0, 255]. + * + * The function changes the arguments to point to the RGB value + * corresponding, with the returned values all in the range [0, 255]. + **/ +inline void +hsv_to_rgb_int (int *hue /* returns red */, + int *saturation /* returns green */, + int *value /* returns blue */) +{ + double h, s, v, h_temp; + double f, p, q, t; + int i; + + if (*saturation == 0) + { + *hue = *value; + *saturation = *value; + // *value = *value; + } + else + { + h = *hue; + s = *saturation / 255.0; + v = *value / 255.0; + + if (h == 360) + h_temp = 0; + else + h_temp = h; + + h_temp = h_temp / 60.0; + i = (int) floor (h_temp); + f = h_temp - i; + p = v * (1.0 - s); + q = v * (1.0 - (s * f)); + t = v * (1.0 - (s * (1.0 - f))); + + switch (i) + { + case 0: + *hue = ROUND (v * 255.0); + *saturation = ROUND (t * 255.0); + *value = ROUND (p * 255.0); + break; + + case 1: + *hue = ROUND (q * 255.0); + *saturation = ROUND (v * 255.0); + *value = ROUND (p * 255.0); + break; + + case 2: + *hue = ROUND (p * 255.0); + *saturation = ROUND (v * 255.0); + *value = ROUND (t * 255.0); + break; + + case 3: + *hue = ROUND (p * 255.0); + *saturation = ROUND (q * 255.0); + *value = ROUND (v * 255.0); + break; + + case 4: + *hue = ROUND (t * 255.0); + *saturation = ROUND (p * 255.0); + *value = ROUND (v * 255.0); + break; + + case 5: + *hue = ROUND (v * 255.0); + *saturation = ROUND (p * 255.0); + *value = ROUND (q * 255.0); + break; + } + } +} + +/** + * rgb_to_hsl_int + * @red: Red channel, returns Hue channel + * @green: Green channel, returns Lightness channel + * @blue: Blue channel, returns Saturation channel + * + * The arguments are pointers to int representing channel values in the + * RGB colorspace, and the values pointed to are all in the range [0, 255]. + * + * The function changes the arguments to point to the corresponding HLS + * value with the values pointed to in the following ranges: H [0, 360], + * L [0, 255], S [0, 255]. + **/ +inline void +rgb_to_hsl_int (unsigned int *red /* returns red */, + unsigned int *green /* returns green */, + unsigned int *blue /* returns blue */) +{ + unsigned int r, g, b; + double h, s, l; + unsigned int min, max; + unsigned int delta; + + r = *red; + g = *green; + b = *blue; + + if (r > g) + { + max = MAX (r, b); + min = MIN (g, b); + } + else + { + max = MAX (g, b); + min = MIN (r, b); + } + + l = (max + min) / 2.0; + + if (max == min) + { + s = 0.0; + h = 0.0; + } + else + { + delta = (max - min); + + if (l < 128) + s = 255 * (double) delta / (double) (max + min); + else + s = 255 * (double) delta / (double) (511 - max - min); + + if (r == max) + h = (g - b) / (double) delta; + else if (g == max) + h = 2 + (b - r) / (double) delta; + else + h = 4 + (r - g) / (double) delta; + + h = h * 42.5; + + if (h < 0) + h += 255; + else if (h > 255) + h -= 255; + } + + *red = ROUND (h); + *green = ROUND (s); + *blue = ROUND (l); +} + +inline int +hsl_value_int (double n1, + double n2, + double hue) +{ + double value; + + if (hue > 255) + hue -= 255; + else if (hue < 0) + hue += 255; + + if (hue < 42.5) + value = n1 + (n2 - n1) * (hue / 42.5); + else if (hue < 127.5) + value = n2; + else if (hue < 170) + value = n1 + (n2 - n1) * ((170 - hue) / 42.5); + else + value = n1; + + return ROUND (value * 255.0); +} + +/** + * hsl_to_rgb_int + * @hue: Hue channel, returns Red channel + * @saturation: Saturation channel, returns Green channel + * @lightness: Lightness channel, returns Blue channel + * + * The arguments are pointers to int, with the values pointed to in the + * following ranges: H [0, 360], L [0, 255], S [0, 255]. + * + * The function changes the arguments to point to the RGB value + * corresponding, with the returned values all in the range [0, 255]. + **/ +inline void +hsl_to_rgb_int (unsigned int *hue /* returns red */, + unsigned int *saturation /* returns green */, + unsigned int *lightness /* returns blue */) +{ + double h, s, l; + + h = *hue; + s = *saturation; + l = *lightness; + + if (s == 0) + { + /* achromatic case */ + *hue = (int)l; + *lightness = (int)l; + *saturation = (int)l; + } + else + { + double m1, m2; + + if (l < 128) + m2 = (l * (255 + s)) / 65025.0; + else + m2 = (l + s - (l * s) / 255.0) / 255.0; + + m1 = (l / 127.5) - m2; + + /* chromatic case */ + *hue = hsl_value_int (m1, m2, h + 85); + *saturation = hsl_value_int (m1, m2, h); + *lightness = hsl_value_int (m1, m2, h - 85); + } +} + +/** + * gimp_rgb_to_cmyk_int: + * @red: the red channel; returns the cyan value (0-255) + * @green: the green channel; returns the magenta value (0-255) + * @blue: the blue channel; returns the yellow value (0-255) + * @pullout: the percentage of black to pull out (0-100); returns + * the black value (0-255) + * + * Does a naive conversion from RGB to CMYK colorspace. A simple + * formula that doesn't take any color-profiles into account is used. + * The amount of black pullout how can be controlled via the @pullout + * parameter. A @pullout value of 0 makes this a conversion to CMY. + * A value of 100 causes the maximum amount of black to be pulled out. + **/ +inline void +gimp_rgb_to_cmyk_int (int *red, + int *green, + int *blue, + int *pullout) +{ + int c, m, y; + + c = 255 - *red; + m = 255 - *green; + y = 255 - *blue; + + if (*pullout == 0) + { + *red = c; + *green = m; + *blue = y; + } + else + { + int k = 255; + + if (c < k) k = c; + if (m < k) k = m; + if (y < k) k = y; + + k = (k * CLAMP (*pullout, 0, 100)) / 100; + + *red = ((c - k) << 8) / (256 - k); + *green = ((m - k) << 8) / (256 - k); + *blue = ((y - k) << 8) / (256 - k); + *pullout = k; + } +} + +/** + * gimp_cmyk_to_rgb_int: + * @cyan: the cyan channel; returns the red value (0-255) + * @magenta: the magenta channel; returns the green value (0-255) + * @yellow: the yellow channel; returns the blue value (0-255) + * @black: the black channel (0-255); doesn't change + * + * Does a naive conversion from CMYK to RGB colorspace. A simple + * formula that doesn't take any color-profiles into account is used. + **/ +inline void +cmyk_to_rgb_int (int *cyan, + int *magenta, + int *yellow, + int *black) +{ + int c, m, y, k; + + c = *cyan; + m = *magenta; + y = *yellow; + k = *black; + + if (k) + { + c = ((c * (256 - k)) >> 8) + k; + m = ((m * (256 - k)) >> 8) + k; + y = ((y * (256 - k)) >> 8) + k; + } + + *cyan = 255 - c; + *magenta = 255 - m; + *yellow = 255 - y; +} + + +#endif diff --git a/include/frei0r/math.h b/include/frei0r/math.h new file mode 100644 index 0000000..841a305 --- /dev/null +++ b/include/frei0r/math.h @@ -0,0 +1,74 @@ +#ifndef INCLUDED_FREI0R_MATH_H +#define INCLUDED_FREI0R_MATH_H + +/* + + Code stripped from The Gimp: + INT_MULT(a,b,t) + INT_MULT3(a,b,c,t) + INT_BLEND(a,b,alpha,tmp) + CLAMP + ROUND + MAX255 + + Code stripped from Drone: + CLAMP0255 + SQR +*/ + +/* Clamps a int32-range int between 0 and 255 inclusive. */ +#ifndef CLAMP0255 +static inline unsigned char CLAMP0255(int32_t a) +{ + return (unsigned char) + ( (((-a) >> 31) & a) // 0 if the number was negative + | (255 - a) >> 31); // -1 if the number was greater than 255 +} +#endif + +/* Provided temporary int t, returns a * b / 255 */ +#ifndef INT_MULT +#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) +#endif + +/* This version of INT_MULT3 is very fast, but suffers from some + slight roundoff errors. It returns the correct result 99.987 + percent of the time */ +#ifndef INT_MULT3 +#define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \ + ((((t) >> 7) + (t)) >> 16)) +#endif + +#ifndef INT_BLEND +#define INT_BLEND(a,b,alpha,tmp) (INT_MULT((a) - (b), alpha, tmp) + (b)) +#endif + +#ifndef CLAMP +//! Clamp x at min and max +#define CLAMP(x,min,max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) +#endif + +#ifndef ROUND +//! Round. +#define ROUND(x) ((int32_t)((x)+0.5)) +#endif + +#ifndef SQR +//! Square. +#define SQR(x) ((x) * (x)) +#endif + +#ifndef MAX255 +//! Limit a (0->511) int to 255. +uint8_t MAX255(uint32_t a) { return (uint8_t) (a | ((a & 256) - ((a & 256) >> 8))); } +#endif + +#ifndef MIN +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#endif diff --git a/include/frei0r.hpp b/include/frei0r.hpp index 5eed2dc..ef12be4 100644 --- a/include/frei0r.hpp +++ b/include/frei0r.hpp @@ -101,7 +101,7 @@ { case F0R_PARAM_BOOL : *static_cast(param) - = *static_cast(ptr) > 0.5 ? 1.0 : 0.0; + = *static_cast(ptr) ? 1.0 : 0.0; break; case F0R_PARAM_DOUBLE: *static_cast(param) diff --git a/include/frei0r_cairo.h b/include/frei0r_cairo.h deleted file mode 100644 index 797b1f5..0000000 --- a/include/frei0r_cairo.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * frei0r_cairo.h - * Copyright 2012 Janne Liljeblad - * - * This file is part of Frei0r. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include "frei0r_math.h" - -/** -* String identifiers for gradient types available using Cairo. -*/ -#define GRADIENT_LINEAR "gradient_linear" -#define GRADIENT_RADIAL "gradient_radial" - -/** -* String identifiers for blend modes available using Cairo. -*/ -#define NORMAL "normal" -#define ADD "add" -#define SATURATE "saturate" -#define MULTIPLY "multiply" -#define SCREEN "screen" -#define OVERLAY "overlay" -#define DARKEN "darken" -#define LIGHTEN "lighten" -#define COLORDODGE "colordodge" -#define COLORBURN "colorburn" -#define HARDLIGHT "hardlight" -#define SOFTLIGHT "softlight" -#define DIFFERENCE "difference" -#define EXCLUSION "exclusion" -#define HSLHUE "hslhue" -#define HSLSATURATION "hslsaturation" -#define HSLCOLOR "hslcolor" -#define HSLLUMINOSITY "hslluminosity" - - -/** -* frei0r_cairo_set_operator -* @cr: Cairo context -* @op: String identifier for a blend mode -* -* Sets cairo context to use the defined blend mode for all paint operations. -*/ -void frei0r_cairo_set_operator(cairo_t *cr, char *op) -{ - if(strcmp(op, NORMAL) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - } - else if(strcmp(op, ADD) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_ADD); - } - else if(strcmp(op, SATURATE) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); - } - else if(strcmp(op, MULTIPLY) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY); - } - else if(strcmp(op, SCREEN) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_SCREEN); - } - else if(strcmp(op, OVERLAY) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY); - } - else if(strcmp(op, DARKEN) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN); - } - else if(strcmp(op, LIGHTEN) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_LIGHTEN); - } - else if(strcmp(op, COLORDODGE) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE); - } - else if(strcmp(op, COLORBURN) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_BURN); - } - else if(strcmp(op, HARDLIGHT) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_HARD_LIGHT); - } - else if(strcmp(op, SOFTLIGHT) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT); - } - else if(strcmp(op, DIFFERENCE) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE); - } - else if(strcmp(op, EXCLUSION) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_EXCLUSION); - } - else if(strcmp(op, HSLHUE) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_HSL_HUE); - } - else if(strcmp(op, HSLSATURATION) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION); - } - else if(strcmp(op, HSLCOLOR) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_HSL_COLOR); - } - else if(strcmp(op, HSLLUMINOSITY ) == 0) - { - cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY); - } - else - { - cairo_set_operator (cr, CAIRO_OPERATOR_OVER); - } -} - - -/** -* frei0r_cairo_set_rgba_LITTLE_ENDIAN -* @cr: Cairo context -* @red: red component, 0 - 1 -* @green: green component, 0 - 1 -* @blue: blue component, 0 - 1 -* @alpha: opacity of color, 0 -1 -* -* Sets cairo context to use the defined color paint operations. -* Switches red and blue channels to get correct color on little endian machines. -* This method only works correctly on little endian machines. -*/ -void frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue, double alpha) -{ - cairo_set_source_rgba (cr, blue, green, red, alpha); -} - -/** -* frei0r_cairo_set_rgb_LITTLE_ENDIAN -* @cr: Cairo context -* @red: red component, 0 - 1 -* @green: green component, 0 - 1 -* @blue: blue component, 0 - 1 -* -* Sets cairo context to use the defined color paint operations. -* Switches red and blue channels to get correct color on little endian machines. -* This method only works correctly on little endian machines. -*/ -void frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue) -{ - cairo_set_source_rgb (cr, blue, green, red); -} - -/** -* freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN( -* @pat: Cairo pattern -* @offset: offset of color position in pattern space, 0 - 1 -* @red: red component, 0 - 1 -* @green: green component, 0 - 1 -* @blue: blue component, 0 - 1 -* @alpha: opacity of color, 0 -1 -* -* Sets color stop for cairo patterns. -* Switches red and blue channels to get correct color on little endian machines. -* This method only works correctly on little endian machines. -*/ -void freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t *pat, double offset, - double red, double green, double blue, double alpha) -{ - cairo_pattern_add_color_stop_rgba (pat, offset, blue, green, red, alpha); -} - -/** -* frei0r_cairo_get_pixel_position -* @norm_pos: position in range 0 - 1, either x or y -* @dim: dimension, either witdh or height -* -* Converts double range [0 -> 1] to pixel range [-2*dim -> 3*dim]. Input 0.4 gives position 0. -* -* Returns: position in pixels -*/ -double frei0r_cairo_get_pixel_position (double norm_pos, int dim) -{ - double pos_o = -(dim * 2.0); - return pos_o + norm_pos * dim * 5.0; -} - -/** -* frei0r_cairo_get_scale -* @norm_scale: scale in range 0 - 1 -* -* Converts double range [0 -> 1] to scale range [0 -> 5]. Input 0.2 gives scale 1.0. -* -* Returns: scale -*/ -double frei0r_cairo_get_scale (double norm_scale) -{ - return norm_scale * 5.0; -} - -/** - * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. - * - * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 - * \param pixels the size of the image buffer in number of pixels - * \param alpha if >= 0, the alpha channel will be set to this value - * \see frei0r_cairo_unpremultiply_rgba - */ -void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) -{ - int i = pixels + 1; - while ( --i ) { - register unsigned char a = rgba[3]; - if (a == 0) { - *((uint32_t *)rgba) = 0; - } else if (a < 0xff) { - rgba[0] = ( rgba[0] * a ) >> 8; - rgba[1] = ( rgba[1] * a ) >> 8; - rgba[2] = ( rgba[2] * a ) >> 8; - } - if (alpha >= 0) rgba[3] = alpha; - rgba += 4; - } -} - -/** - * Convert Cairo ARGB pre-multiplied alpha to frei0r straight RGBA. - * - * \param rgba the image buffer with format CAIRO_FORMAT_ARGB32 - * \param pixels the size of the image buffer in number of pixels - * \see frei0r_cairo_premultiply_rgba - */ -void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) -{ - int i = pixels + 1; - while ( --i ) { - register unsigned char a = rgba[3]; - if (a > 0 && a < 0xff) { - rgba[0] = MIN(( rgba[0] << 8 ) / a, 255); - rgba[1] = MIN(( rgba[1] << 8 ) / a, 255); - rgba[2] = MIN(( rgba[2] << 8 ) / a, 255); - } - rgba += 4; - } -} - -/** - * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. - * - * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 - * \param pixels the size of the image buffer in number of pixels - * \param alpha if >= 0, the alpha channel will be set to this value - * \see frei0r_cairo_premultiply_rgba - * - * This is the same as frei0r_cairo_premultiply_rgba but it writes the - * output to a different buffer. - */ -void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, - int pixels, int alpha) -{ - int i = pixels + 1; - while ( --i ) { - register unsigned char a = in[3]; - if (a == 0) { - *((uint32_t *)out) = 0; - } else if (a == 0xff) { - memcpy(out, in, 4); - } else { - out[0] = ( in[0] * a ) >> 8; - out[1] = ( in[1] * a ) >> 8; - out[2] = ( in[2] * a ) >> 8; - if (alpha < 0) - out[3] = a; - } - if (alpha >= 0) - out[3] = alpha; - in += 4; - out += 4; - } -} diff --git a/include/frei0r_cfc.h b/include/frei0r_cfc.h deleted file mode 100644 index 14cca12..0000000 --- a/include/frei0r_cfc.h +++ /dev/null @@ -1,319 +0,0 @@ -/* cfc.h - * uchar->float and float->uchar conversion for packed RGBA video - * with flexible gamma correction - * - * Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu - * This file is a part of the Frei0r package - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -// the float to uint8_t conversion is done -// using Spitzak type tables (upper 16 bits -// of a float value used as table index) -// see http://mysite.verizon.net/spitzak/conversion/ - -#include -#include - - -typedef struct - { - float r; - float g; - float b; - float a; - } float_rgba; - -//------------------------------------------------------ -//the following gamma functions need not be speed optimized, -//as they are only used for table generation - -//-------------------------------------------------------- -//rec 709 gamma forward (linear to gamma) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_709_f(float a) -{ -return (a<=0.018) ? 4.5*a : 1.099*powf(a,0.45)-0.099; -} - -//-------------------------------------------------------- -//rec 709 gamma backward (gamma to linear) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_709_b(float a) -{ -return (a<=0.081) ? a/4.5 : powf((a+0.099)/1.099,1.0/0.45); -} - -//---------------------------------------------------- -//sRGB gamma forward (linear to gamma) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_sRGB_f(float a) -{ -return (a<=0.0031308) ? 12.92*a : 1.055*powf(a,1.0/2.4)-0.055; -} - -//-------------------------------------------------------- -//sRGB gamma backward (gamma to linear) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_sRGB_b(float a) -{ -return (a<=0.04045) ? a/12.92 : powf((a+0.055)/1.055,2.4); -} - -//---------------------------------------------------- -//plain gamma (power function) forward (linear to gamma) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_plain_f(float a, float g) -{ -return powf(a,1.0/g); -} - -//-------------------------------------------------------- -//plain gamma (power function) backward (gamma to linear) -//a = input, should be in the 0.0 to 1.0 range -static inline float gamma_plain_b(float a, float g) -{ -return powf(a,g); -} - -//-------------------------------------------------------- -//dummy function for linear tables (no gamma) -//g = gamma value -static inline float gamma_none(float a) -{ -return a; -} - -//------------------------------------------------ -//expand highlights using a modified Spitzak formula -//with limited max output value (250) -//(for values up to 2500 use 1.0001 and 0.493) -//input [0...1] -//output [0...250] -static inline float exp_hl(float a) -{ -return (a<=0.4781) ? a : 0.25/(1.001-a); -} - -//------------------------------------------------------------ -//compress highlights using a modified Spitzak formula -//with limited max input value (250) -//(for values up to 2500 use 1.0001 and 0.493) -//input [0...250] -//output [0...1] -static inline float cps_hl(float a) -{ -return (a<=0.4781) ? a : 1.001-0.25/a; -} - -//---------------------------------------------------------- -//float to char conversion is done using the upper 16 bits -//of the float number as an index into the conversion table. -//This union is used for type punning, to avoid problems -//with compiler optimizations, as the read access in the -//cfc_tab_8 function directly follows writing -typedef union - { - float a; - uint16_t i[2]; - } flint; - -//-------------------------------------------------------- -//generate forward and backward conversion tables -//for 8 bit (uint8_t) video -//*ft = forward table (float to uchar, linear to gamma) -// must have space for 65536 char elements -//*bt = backward table (uchar to float, gamma to linear) -// must have space for 256 float elements -//type = what kind of gamma function will be used: -// 0 = linear (no gamma, just multiply/divide by 255) -// 1 = plain gamma (pure power function) -// 2 = rec 709 gamma -// 3 = sRGB gamma -// 4 = sRGB gamma with highlight expansion/compression -// types 0...3 map to [0...1] linear float range -// type 4 maps to [0...250] linear float range -//g = gamma value, 0.3 to 3.0 (only used with type=1) -static inline void cfc_tab_8(uint8_t *ft, float *bt, int type, float g) -{ -uint16_t i; -float a; -flint fi; - -// *** generate float to char conversion table *** -for (i=0;i<128;i++) //positive denormals map to zero - ft[i]=0; - -for (i=128;i<=32639;i++) //positive numbers map to 0...255 - { -//#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN -// fi.i[0]=i; fi.i[1]=0x8000; //big endian -//#endif -//#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN - fi.i[1]=i; fi.i[0]=0x8000; //little endian -//#endif - a=fi.a; - switch (type) - { - case 0: a=gamma_none(a); break; - case 1: a=gamma_plain_f(a,g); break; - case 2: a=gamma_709_f(a); break; - case 3: a=gamma_sRGB_f(a); break; - case 4: a=cps_hl(a); a=gamma_sRGB_f(a); break; - default: break; - } - if (a>0.9999) a=0.9999; - ft[i]=(uint8_t)(256.0*a); - } - -for (i=32640;i<32768;i++) //positive NANs and infinite map to 255 - ft[i]=255; -for (i=32768;i!=65535;i++) //everything negative maps to 0 - ft[i]=0; -ft[65535]=0; - - -// *** generate char to float conversion table *** -for (i=0;i<=255;i++) - { - a=((float)i+0.5)/256.0; - switch (type) - { - case 0: a=gamma_none(a); break; - case 1: a=gamma_plain_b(a,g); break; - case 2: a=gamma_709_b(a); break; - case 3: a=gamma_sRGB_b(a); break; - case 4: a=gamma_sRGB_b(a); a=exp_hl(a); break; - default: break; - } - bt[i]=a; - } -} - -//-------------------------------------------------------- -//convert from paked uchar RGBA to packed float RGBA -//w,h are width and height of the image -//tab = table used for RGB conversion -//atab = table used for alpha converion (usually linear) -static inline void RGBA8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab, float *atab) -{ -int i; -uint8_t *cin=(uint8_t *)in; - -for (i=0;ii[1]]; -} -//endif - -//#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN -//static inline float_2_uint8(const float *in, uint8_t *tab) -//{ -//return tab[((flint*)in)->i[0]]; -//} -//#endif diff --git a/include/frei0r_colorspace.h b/include/frei0r_colorspace.h deleted file mode 100644 index 808859e..0000000 --- a/include/frei0r_colorspace.h +++ /dev/null @@ -1,416 +0,0 @@ -/* frei0r_colorspace.h - * Copyright (C) 2004 Mathieu Guindon, Julien Keable, Jean-Sebastien Senecal - * This file is part of Frei0r. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef INCLUDED_FREI0R_COLORSPACE_H -#define INCLUDED_FREI0R_COLORSPACE_H - -#include "frei0r_math.h" -#include -#include - -// # Basic colorspace convert functions (from the Gimp gimpcolorspace.h) #### - -/* int functions */ - -/** - * rgb_to_hsv_int - * @red: The red channel value, returns the Hue channel - * @green: The green channel value, returns the Saturation channel - * @blue: The blue channel value, returns the Value channel - * - * The arguments are pointers to int representing channel values in - * the RGB colorspace, and the values pointed to are all in the range - * [0, 255]. - * - * The function changes the arguments to point to the HSV value - * corresponding, with the returned values in the following - * ranges: H [0, 360], S [0, 255], V [0, 255]. - **/ -inline void -rgb_to_hsv_int (int *red /* returns hue */, - int *green /* returns saturation */, - int *blue /* returns value */) -{ - double r, g, b; - double h, s, v; - double min; - double delta; - - r = *red; - g = *green; - b = *blue; - - if (r > g) - { - v = MAX (r, b); - min = MIN (g, b); - } - else - { - v = MAX (g, b); - min = MIN (r, b); - } - - delta = v - min; - - if (v == 0.0) - s = 0.0; - else - s = delta / v; - - if (s == 0.0) - h = 0.0; - else - { - if (r == v) - h = 60.0 * (g - b) / delta; - else if (g == v) - h = 120 + 60.0 * (b - r) / delta; - else - h = 240 + 60.0 * (r - g) / delta; - - if (h < 0.0) - h += 360.0; - if (h > 360.0) - h -= 360.0; - } - - *red = ROUND (h); - *green = ROUND (s * 255.0); - *blue = ROUND (v); -} - -/** - * hsv_to_rgb_int - * @hue: The hue channel, returns the red channel - * @saturation: The saturation channel, returns the green channel - * @value: The value channel, returns the blue channel - * - * The arguments are pointers to int, with the values pointed to in the - * following ranges: H [0, 360], S [0, 255], V [0, 255]. - * - * The function changes the arguments to point to the RGB value - * corresponding, with the returned values all in the range [0, 255]. - **/ -inline void -hsv_to_rgb_int (int *hue /* returns red */, - int *saturation /* returns green */, - int *value /* returns blue */) -{ - double h, s, v, h_temp; - double f, p, q, t; - int i; - - if (*saturation == 0) - { - *hue = *value; - *saturation = *value; - // *value = *value; - } - else - { - h = *hue; - s = *saturation / 255.0; - v = *value / 255.0; - - if (h == 360) - h_temp = 0; - else - h_temp = h; - - h_temp = h_temp / 60.0; - i = (int) floor (h_temp); - f = h_temp - i; - p = v * (1.0 - s); - q = v * (1.0 - (s * f)); - t = v * (1.0 - (s * (1.0 - f))); - - switch (i) - { - case 0: - *hue = ROUND (v * 255.0); - *saturation = ROUND (t * 255.0); - *value = ROUND (p * 255.0); - break; - - case 1: - *hue = ROUND (q * 255.0); - *saturation = ROUND (v * 255.0); - *value = ROUND (p * 255.0); - break; - - case 2: - *hue = ROUND (p * 255.0); - *saturation = ROUND (v * 255.0); - *value = ROUND (t * 255.0); - break; - - case 3: - *hue = ROUND (p * 255.0); - *saturation = ROUND (q * 255.0); - *value = ROUND (v * 255.0); - break; - - case 4: - *hue = ROUND (t * 255.0); - *saturation = ROUND (p * 255.0); - *value = ROUND (v * 255.0); - break; - - case 5: - *hue = ROUND (v * 255.0); - *saturation = ROUND (p * 255.0); - *value = ROUND (q * 255.0); - break; - } - } -} - -/** - * rgb_to_hsl_int - * @red: Red channel, returns Hue channel - * @green: Green channel, returns Lightness channel - * @blue: Blue channel, returns Saturation channel - * - * The arguments are pointers to int representing channel values in the - * RGB colorspace, and the values pointed to are all in the range [0, 255]. - * - * The function changes the arguments to point to the corresponding HLS - * value with the values pointed to in the following ranges: H [0, 360], - * L [0, 255], S [0, 255]. - **/ -inline void -rgb_to_hsl_int (unsigned int *red /* returns red */, - unsigned int *green /* returns green */, - unsigned int *blue /* returns blue */) -{ - unsigned int r, g, b; - double h, s, l; - unsigned int min, max; - unsigned int delta; - - r = *red; - g = *green; - b = *blue; - - if (r > g) - { - max = MAX (r, b); - min = MIN (g, b); - } - else - { - max = MAX (g, b); - min = MIN (r, b); - } - - l = (max + min) / 2.0; - - if (max == min) - { - s = 0.0; - h = 0.0; - } - else - { - delta = (max - min); - - if (l < 128) - s = 255 * (double) delta / (double) (max + min); - else - s = 255 * (double) delta / (double) (511 - max - min); - - if (r == max) - h = (g - b) / (double) delta; - else if (g == max) - h = 2 + (b - r) / (double) delta; - else - h = 4 + (r - g) / (double) delta; - - h = h * 42.5; - - if (h < 0) - h += 255; - else if (h > 255) - h -= 255; - } - - *red = ROUND (h); - *green = ROUND (s); - *blue = ROUND (l); -} - -inline int -hsl_value_int (double n1, - double n2, - double hue) -{ - double value; - - if (hue > 255) - hue -= 255; - else if (hue < 0) - hue += 255; - - if (hue < 42.5) - value = n1 + (n2 - n1) * (hue / 42.5); - else if (hue < 127.5) - value = n2; - else if (hue < 170) - value = n1 + (n2 - n1) * ((170 - hue) / 42.5); - else - value = n1; - - return ROUND (value * 255.0); -} - -/** - * hsl_to_rgb_int - * @hue: Hue channel, returns Red channel - * @saturation: Saturation channel, returns Green channel - * @lightness: Lightness channel, returns Blue channel - * - * The arguments are pointers to int, with the values pointed to in the - * following ranges: H [0, 360], L [0, 255], S [0, 255]. - * - * The function changes the arguments to point to the RGB value - * corresponding, with the returned values all in the range [0, 255]. - **/ -inline void -hsl_to_rgb_int (unsigned int *hue /* returns red */, - unsigned int *saturation /* returns green */, - unsigned int *lightness /* returns blue */) -{ - double h, s, l; - - h = *hue; - s = *saturation; - l = *lightness; - - if (s == 0) - { - /* achromatic case */ - *hue = (int)l; - *lightness = (int)l; - *saturation = (int)l; - } - else - { - double m1, m2; - - if (l < 128) - m2 = (l * (255 + s)) / 65025.0; - else - m2 = (l + s - (l * s) / 255.0) / 255.0; - - m1 = (l / 127.5) - m2; - - /* chromatic case */ - *hue = hsl_value_int (m1, m2, h + 85); - *saturation = hsl_value_int (m1, m2, h); - *lightness = hsl_value_int (m1, m2, h - 85); - } -} - -/** - * gimp_rgb_to_cmyk_int: - * @red: the red channel; returns the cyan value (0-255) - * @green: the green channel; returns the magenta value (0-255) - * @blue: the blue channel; returns the yellow value (0-255) - * @pullout: the percentage of black to pull out (0-100); returns - * the black value (0-255) - * - * Does a naive conversion from RGB to CMYK colorspace. A simple - * formula that doesn't take any color-profiles into account is used. - * The amount of black pullout how can be controlled via the @pullout - * parameter. A @pullout value of 0 makes this a conversion to CMY. - * A value of 100 causes the maximum amount of black to be pulled out. - **/ -inline void -gimp_rgb_to_cmyk_int (int *red, - int *green, - int *blue, - int *pullout) -{ - int c, m, y; - - c = 255 - *red; - m = 255 - *green; - y = 255 - *blue; - - if (*pullout == 0) - { - *red = c; - *green = m; - *blue = y; - } - else - { - int k = 255; - - if (c < k) k = c; - if (m < k) k = m; - if (y < k) k = y; - - k = (k * CLAMP (*pullout, 0, 100)) / 100; - - *red = ((c - k) << 8) / (256 - k); - *green = ((m - k) << 8) / (256 - k); - *blue = ((y - k) << 8) / (256 - k); - *pullout = k; - } -} - -/** - * gimp_cmyk_to_rgb_int: - * @cyan: the cyan channel; returns the red value (0-255) - * @magenta: the magenta channel; returns the green value (0-255) - * @yellow: the yellow channel; returns the blue value (0-255) - * @black: the black channel (0-255); doesn't change - * - * Does a naive conversion from CMYK to RGB colorspace. A simple - * formula that doesn't take any color-profiles into account is used. - **/ -inline void -cmyk_to_rgb_int (int *cyan, - int *magenta, - int *yellow, - int *black) -{ - int c, m, y, k; - - c = *cyan; - m = *magenta; - y = *yellow; - k = *black; - - if (k) - { - c = ((c * (256 - k)) >> 8) + k; - m = ((m * (256 - k)) >> 8) + k; - y = ((y * (256 - k)) >> 8) + k; - } - - *cyan = 255 - c; - *magenta = 255 - m; - *yellow = 255 - y; -} - - -#endif diff --git a/include/frei0r_math.h b/include/frei0r_math.h deleted file mode 100644 index 841a305..0000000 --- a/include/frei0r_math.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef INCLUDED_FREI0R_MATH_H -#define INCLUDED_FREI0R_MATH_H - -/* - - Code stripped from The Gimp: - INT_MULT(a,b,t) - INT_MULT3(a,b,c,t) - INT_BLEND(a,b,alpha,tmp) - CLAMP - ROUND - MAX255 - - Code stripped from Drone: - CLAMP0255 - SQR -*/ - -/* Clamps a int32-range int between 0 and 255 inclusive. */ -#ifndef CLAMP0255 -static inline unsigned char CLAMP0255(int32_t a) -{ - return (unsigned char) - ( (((-a) >> 31) & a) // 0 if the number was negative - | (255 - a) >> 31); // -1 if the number was greater than 255 -} -#endif - -/* Provided temporary int t, returns a * b / 255 */ -#ifndef INT_MULT -#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) -#endif - -/* This version of INT_MULT3 is very fast, but suffers from some - slight roundoff errors. It returns the correct result 99.987 - percent of the time */ -#ifndef INT_MULT3 -#define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \ - ((((t) >> 7) + (t)) >> 16)) -#endif - -#ifndef INT_BLEND -#define INT_BLEND(a,b,alpha,tmp) (INT_MULT((a) - (b), alpha, tmp) + (b)) -#endif - -#ifndef CLAMP -//! Clamp x at min and max -#define CLAMP(x,min,max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) -#endif - -#ifndef ROUND -//! Round. -#define ROUND(x) ((int32_t)((x)+0.5)) -#endif - -#ifndef SQR -//! Square. -#define SQR(x) ((x) * (x)) -#endif - -#ifndef MAX255 -//! Limit a (0->511) int to 255. -uint8_t MAX255(uint32_t a) { return (uint8_t) (a | ((a & 256) - ((a & 256) >> 8))); } -#endif - -#ifndef MIN -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#endif - -#ifndef MAX -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -#endif diff --git a/src/filter/CMakeLists.txt b/src/filter/CMakeLists.txt index 0c22852..804fcda 100644 --- a/src/filter/CMakeLists.txt +++ b/src/filter/CMakeLists.txt @@ -12,6 +12,7 @@ if (${Cairo_FOUND}) add_subdirectory (cairoimagegrid) add_subdirectory (cairogradient) + add_subdirectory (mirr0r) endif (${Cairo_FOUND}) add_subdirectory (3dflippo) @@ -69,6 +70,7 @@ add_subdirectory (nervous) add_subdirectory (normaliz0r) add_subdirectory (nosync0r) +add_subdirectory (ntsc) add_subdirectory (perspective) add_subdirectory (pixeliz0r) add_subdirectory (pixs0r) diff --git a/src/filter/aech0r/aech0r.cpp b/src/filter/aech0r/aech0r.cpp index f628ac8..7aa005a 100644 --- a/src/filter/aech0r/aech0r.cpp +++ b/src/filter/aech0r/aech0r.cpp @@ -22,7 +22,7 @@ * */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include #include diff --git a/src/filter/alpha0ps/alpha0ps_alphaspot.c b/src/filter/alpha0ps/alpha0ps_alphaspot.c index 2bb8c5b..4dacf64 100644 --- a/src/filter/alpha0ps/alpha0ps_alphaspot.c +++ b/src/filter/alpha0ps/alpha0ps_alphaspot.c @@ -29,7 +29,7 @@ //#include #include -#include +#include #include #include #include diff --git a/src/filter/balanc0r/balanc0r.c b/src/filter/balanc0r/balanc0r.c index c52bc15..3914fbf 100644 --- a/src/filter/balanc0r/balanc0r.c +++ b/src/filter/balanc0r/balanc0r.c @@ -28,7 +28,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" static const float bbWB[][3] = { diff --git a/src/filter/blur/fibe.h b/src/filter/blur/fibe.h index 16df5a7..5c317c3 100755 --- a/src/filter/blur/fibe.h +++ b/src/filter/blur/fibe.h @@ -69,7 +69,7 @@ #include #include -#include "frei0r_math.h" +#include "frei0r/math.h" //--------------------------------------------------------- //koeficienti za biquad lowpass iz f in q diff --git a/src/filter/brightness/brightness.c b/src/filter/brightness/brightness.c index e0f867c..1738a4d 100644 --- a/src/filter/brightness/brightness.c +++ b/src/filter/brightness/brightness.c @@ -21,7 +21,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct brightness_instance { diff --git a/src/filter/c0rners/c0rners.c b/src/filter/c0rners/c0rners.c index c444f77..a6f26ba 100644 --- a/src/filter/c0rners/c0rners.c +++ b/src/filter/c0rners/c0rners.c @@ -28,7 +28,7 @@ #include #include #include -#include "frei0r_math.h" +#include "frei0r/math.h" #include "interp.h" //---------------------------------------- diff --git a/src/filter/cairogradient/cairogradient.c b/src/filter/cairogradient/cairogradient.c index 9e10428..e01cd70 100644 --- a/src/filter/cairogradient/cairogradient.c +++ b/src/filter/cairogradient/cairogradient.c @@ -27,7 +27,7 @@ #include #include "frei0r.h" -#include "frei0r_cairo.h" +#include "frei0r/cairo.h" typedef struct cairo_gradient_instance { diff --git a/src/filter/cairoimagegrid/cairoimagegrid.c b/src/filter/cairoimagegrid/cairoimagegrid.c index f25100d..8330a11 100644 --- a/src/filter/cairoimagegrid/cairoimagegrid.c +++ b/src/filter/cairoimagegrid/cairoimagegrid.c @@ -24,7 +24,7 @@ #include #include "frei0r.h" -#include "frei0r_cairo.h" +#include "frei0r/cairo.h" #define MAX_ROWS 20 #define MAX_COLUMNS 20 diff --git a/src/filter/cluster/cluster.c b/src/filter/cluster/cluster.c index a57098a..fdb165b 100644 --- a/src/filter/cluster/cluster.c +++ b/src/filter/cluster/cluster.c @@ -24,7 +24,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAXNUM 40 diff --git a/src/filter/colgate/colgate.c b/src/filter/colgate/colgate.c index 4414d4d..fad7b26 100644 --- a/src/filter/colgate/colgate.c +++ b/src/filter/colgate/colgate.c @@ -88,7 +88,7 @@ #endif #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" enum ParamIndex { NEUTRAL_COLOR, diff --git a/src/filter/colordistance/colordistance.c b/src/filter/colordistance/colordistance.c index 6c68ff3..51866b4 100644 --- a/src/filter/colordistance/colordistance.c +++ b/src/filter/colordistance/colordistance.c @@ -23,7 +23,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct colordistance_instance { diff --git a/src/filter/colorhalftone/colorhalftone.c b/src/filter/colorhalftone/colorhalftone.c index 995fbba..9bc0dde 100644 --- a/src/filter/colorhalftone/colorhalftone.c +++ b/src/filter/colorhalftone/colorhalftone.c @@ -27,7 +27,7 @@ #include #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" double PI=3.14159265358979; diff --git a/src/filter/colorize/colorize.c b/src/filter/colorize/colorize.c index 2341531..8a90809 100644 --- a/src/filter/colorize/colorize.c +++ b/src/filter/colorize/colorize.c @@ -32,7 +32,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define GIMP_RGB_LUMINANCE_RED (0.2126) #define GIMP_RGB_LUMINANCE_GREEN (0.7152) diff --git a/src/filter/contrast0r/contrast0r.c b/src/filter/contrast0r/contrast0r.c index f171bcc..22efb05 100644 --- a/src/filter/contrast0r/contrast0r.c +++ b/src/filter/contrast0r/contrast0r.c @@ -22,7 +22,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct contrast0r_instance { diff --git a/src/filter/curves/curves.c b/src/filter/curves/curves.c index 2e80a3a..fc8d060 100644 --- a/src/filter/curves/curves.c +++ b/src/filter/curves/curves.c @@ -28,7 +28,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAX3(a, b, c) ( ( a > b && a > c) ? a : (b > c ? b : c) ) #define MIN3(a, b, c) ( ( a < b && a < c) ? a : (b < c ? b : c) ) diff --git a/src/filter/dither/dither.c b/src/filter/dither/dither.c index 8d4aa18..36e5bfc 100644 --- a/src/filter/dither/dither.c +++ b/src/filter/dither/dither.c @@ -27,7 +27,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" int ditherMagic2x2Matrix[] = { 0, 2, diff --git a/src/filter/edgeglow/edgeglow.cpp b/src/filter/edgeglow/edgeglow.cpp index eeb5695..82f091f 100644 --- a/src/filter/edgeglow/edgeglow.cpp +++ b/src/filter/edgeglow/edgeglow.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include class edgeglow : public frei0r::filter diff --git a/src/filter/emboss/emboss.c b/src/filter/emboss/emboss.c index b918979..6009fcb 100644 --- a/src/filter/emboss/emboss.c +++ b/src/filter/emboss/emboss.c @@ -27,7 +27,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" double PI = 3.14159; double pixelScale = 255.9; diff --git a/src/filter/equaliz0r/equaliz0r.cpp b/src/filter/equaliz0r/equaliz0r.cpp index bafd040..be33820 100644 --- a/src/filter/equaliz0r/equaliz0r.cpp +++ b/src/filter/equaliz0r/equaliz0r.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include #include diff --git a/src/filter/facebl0r/facebl0r.cpp b/src/filter/facebl0r/facebl0r.cpp index fa21a35..0e5c844 100644 --- a/src/filter/facebl0r/facebl0r.cpp +++ b/src/filter/facebl0r/facebl0r.cpp @@ -20,7 +20,7 @@ #include #include #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" class TrackedObj { public: diff --git a/src/filter/facedetect/facedetect.cpp b/src/filter/facedetect/facedetect.cpp index 580b53b..c27b458 100644 --- a/src/filter/facedetect/facedetect.cpp +++ b/src/filter/facedetect/facedetect.cpp @@ -22,7 +22,7 @@ #include #include #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define USE_ROI #define PAD (40) diff --git a/src/filter/filmgrain/filmgrain.c b/src/filter/filmgrain/filmgrain.c index dc80989..2439e5a 100644 --- a/src/filter/filmgrain/filmgrain.c +++ b/src/filter/filmgrain/filmgrain.c @@ -1,6 +1,6 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct flimgrain_instance diff --git a/src/filter/gamma/gamma.c b/src/filter/gamma/gamma.c index 7c737fe..b3f3422 100644 --- a/src/filter/gamma/gamma.c +++ b/src/filter/gamma/gamma.c @@ -22,7 +22,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAX_GAMMA 4.0 diff --git a/src/filter/gateweave/gateweave.c b/src/filter/gateweave/gateweave.c index 2a5fb4f..eef3935 100644 --- a/src/filter/gateweave/gateweave.c +++ b/src/filter/gateweave/gateweave.c @@ -1,7 +1,7 @@ #include #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" // let's try to walk through everything because i'm a moron diff --git a/src/filter/glitch0r/glitch0r.c b/src/filter/glitch0r/glitch0r.c index c887a3e..34264d0 100644 --- a/src/filter/glitch0r/glitch0r.c +++ b/src/filter/glitch0r/glitch0r.c @@ -25,7 +25,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" /* cheap & fast randomizer (by Fukuchi Kentarou) */ static uint32_t randval; diff --git a/src/filter/glow/glow.c b/src/filter/glow/glow.c index b0572f1..e545ac5 100644 --- a/src/filter/glow/glow.c +++ b/src/filter/glow/glow.c @@ -20,7 +20,7 @@ #include #include "frei0r.h" #include -#include "blur.h" +#include "frei0r/blur.h" typedef struct glow_instance { double blur; diff --git a/src/filter/hueshift0r/matrix.h b/src/filter/hueshift0r/matrix.h index 7d52ab9..2331ef6 100644 --- a/src/filter/hueshift0r/matrix.h +++ b/src/filter/hueshift0r/matrix.h @@ -7,7 +7,7 @@ * * Paul Haeberli - 1993 */ -#include "frei0r_math.h" +#include "frei0r/math.h" #include #include diff --git a/src/filter/lenscorrection/lenscorrection.c b/src/filter/lenscorrection/lenscorrection.c index 85a4a54..52bc355 100644 --- a/src/filter/lenscorrection/lenscorrection.c +++ b/src/filter/lenscorrection/lenscorrection.c @@ -22,7 +22,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct lenscorrection_instance { diff --git a/src/filter/levels/CMakeLists.txt b/src/filter/levels/CMakeLists.txt index 081fcd8..13018f4 100644 --- a/src/filter/levels/CMakeLists.txt +++ b/src/filter/levels/CMakeLists.txt @@ -1,6 +1,6 @@ # Set C99 flag for gcc if (CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "-std=c99") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") endif (CMAKE_COMPILER_IS_GNUCC) set (SOURCES levels.c) diff --git a/src/filter/levels/levels.c b/src/filter/levels/levels.c index a24d60a..0be3953 100644 --- a/src/filter/levels/levels.c +++ b/src/filter/levels/levels.c @@ -24,7 +24,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" enum ChannelChoice { diff --git a/src/filter/luminance/luminance.c b/src/filter/luminance/luminance.c index 394e0e3..4571133 100644 --- a/src/filter/luminance/luminance.c +++ b/src/filter/luminance/luminance.c @@ -22,7 +22,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAX_SATURATION 8.0 diff --git a/src/filter/mask0mate/mask0mate.c b/src/filter/mask0mate/mask0mate.c index bc1448f..910fdfa 100644 --- a/src/filter/mask0mate/mask0mate.c +++ b/src/filter/mask0mate/mask0mate.c @@ -20,7 +20,7 @@ #include #include "frei0r.h" #include -#include "blur.h" +#include "frei0r/blur.h" typedef struct mask0mate_instance { double left, top, right, bottom; diff --git a/src/filter/mirr0r/CMakeLists.txt b/src/filter/mirr0r/CMakeLists.txt new file mode 100755 index 0000000..7a811bd --- /dev/null +++ b/src/filter/mirr0r/CMakeLists.txt @@ -0,0 +1,15 @@ +set (SOURCES mirr0r.cpp) +set (TARGET mirr0r) + +include_directories(${Cairo_INCLUDE_DIR}) +set(LIBS ${LIBS} ${Cairo_LIBRARY}) + +if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_DEF}) +endif (MSVC) + +add_library (${TARGET} MODULE ${SOURCES}) + +set_target_properties (${TARGET} PROPERTIES PREFIX "") +target_link_libraries(mirr0r ${LIBS}) +install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) diff --git a/src/filter/mirr0r/mirr0r.cpp b/src/filter/mirr0r/mirr0r.cpp new file mode 100755 index 0000000..a6113db --- /dev/null +++ b/src/filter/mirr0r/mirr0r.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 Johann JEG (johannjeg1057@gmail.com) + * + * This file is a Frei0r plugin. + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "frei0r.hpp" +#include +#define _USE_MATH_DEFINES +#include + +class Mirr0r : public frei0r::filter { + +private: + + unsigned int width; + unsigned int height; + double x_offset; + double y_offset; + double zoom; + double rotation; + +public: + + Mirr0r(unsigned int width, unsigned int height){ + register_param(this->x_offset, "x_offset", "Horizontal offset for image positioning."); + register_param(this->y_offset, "y_offset", "Vertical offset for image positioning."); + register_param(this->zoom, "zoom", "Zoom level for image scaling."); + register_param(this->rotation, "rotation", "Rotation angle in degrees."); + + this->width = width; + this->height = height; + this->x_offset = 0.0; + this->y_offset = 0.0; + this->zoom = 0.5; + this->rotation = 0.0; + } + + ~Mirr0r(){ + + } + + void clearScreen(cairo_t* cr, int width, int height) { + cairo_save (cr); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_restore (cr); + } + + virtual void update(double time, uint32_t* out, const uint32_t* in) { + + int w = this->width; + int h = this->height; + + // Calculate the stride for the image surface based on width and format + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); + + // Create a Cairo surface for the destination image + cairo_surface_t* dest_image = cairo_image_surface_create_for_data((unsigned char*)out, + CAIRO_FORMAT_ARGB32, + w, + h, + stride); + + // Create a Cairo drawing context for the destination surface + cairo_t *cr = cairo_create(dest_image); + + // Clear the screen + clearScreen(cr, w, h); + + // Create a Cairo surface for the source image + cairo_surface_t *image = cairo_image_surface_create_for_data((unsigned char*)in, + CAIRO_FORMAT_ARGB32, + w, + h, + stride); + // Create a pattern from the source image surface + cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); + // Set the pattern extend mode to reflect (mirror) when the pattern is out of bounds + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); + + // Initialize the transformation matrix + cairo_matrix_t matrix; + cairo_matrix_init_identity(&matrix); + + // Calculate the center coordinates of the destination image + float center_x = (float)w / 2.0f; + float center_y = (float)h / 2.0f; + + float scale_factor = 1.5f - this->zoom; + + // Apply transformations + cairo_matrix_translate(&matrix, center_x - (this->x_offset) * w, center_y - (this->y_offset) * h); + cairo_matrix_scale(&matrix, scale_factor, scale_factor); + cairo_matrix_rotate(&matrix, this->rotation * M_PI); + cairo_matrix_translate(&matrix, -center_x, -center_y); + + // Set the transformation matrix for the pattern + cairo_pattern_set_matrix(pattern, &matrix); + // Set the source pattern to be used for drawing + cairo_set_source(cr, pattern); + + // Draw a rectangle covering the entire image area + cairo_rectangle(cr, 0, 0, w, h); + // Fill the rectangle with the source pattern + cairo_fill(cr); + + // Clean up resources + cairo_pattern_destroy (pattern); + cairo_surface_destroy (image); + cairo_surface_destroy (dest_image); + cairo_destroy (cr); + } +}; + +frei0r::construct plugin( + "Mirr0r", + "Repeats and flips the input image when it goes out of bounds, allowing for adjustable offset, zoom and rotation. A versatile tool for creative video effects.", + "Johann JEG", + 1, 0, + F0R_COLOR_MODEL_RGBA8888); + \ No newline at end of file diff --git a/src/filter/ndvi/ndvi.cpp b/src/filter/ndvi/ndvi.cpp index facdfd6..6b8ea0f 100644 --- a/src/filter/ndvi/ndvi.cpp +++ b/src/filter/ndvi/ndvi.cpp @@ -19,7 +19,7 @@ #include #endif #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include "gradientlut.hpp" #include #include diff --git a/src/filter/normaliz0r/normaliz0r.c b/src/filter/normaliz0r/normaliz0r.c index cd98910..b8be8f8 100644 --- a/src/filter/normaliz0r/normaliz0r.c +++ b/src/filter/normaliz0r/normaliz0r.c @@ -74,7 +74,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAX_HISTORY_LEN 128 diff --git a/src/filter/nosync0r/nosync0r.cpp b/src/filter/nosync0r/nosync0r.cpp index 023b7f6..47e2dcd 100644 --- a/src/filter/nosync0r/nosync0r.cpp +++ b/src/filter/nosync0r/nosync0r.cpp @@ -10,21 +10,38 @@ { hsync = 0.0; register_param(hsync,"HSync","the hsync offset"); + vsync = 0.0; + register_param(vsync,"VSync","the vsync offset"); } virtual void update(double time, uint32_t* out, const uint32_t* in) { - unsigned int - first_line=static_cast(height*std::fmod(std::fabs(hsync),1.0)); + if (hsync == 0.0 && vsync == 0.0) { + std::copy(in, in+width*height, out); + } else if (vsync == 0.0) { + unsigned int + first_line=static_cast(height*std::fmod(std::fabs(hsync),1.0)); - std::copy(in+width*first_line, in+width*height, out); - std::copy(in, in+width*first_line, out+width*(height-first_line)); + std::copy(in+width*first_line, in+width*height, out); + std::copy(in, in+width*first_line, out+width*(height-first_line)); + } else { + unsigned int hoffset=static_cast(height*std::fmod(std::fabs(hsync),1.0)); + unsigned int voffset=static_cast(width*std::fmod(std::fabs(vsync),1.0)); + for (unsigned int src_line = 0; src_line < height; src_line++) { + unsigned int dst_line = (src_line + hoffset) % height; + const uint32_t* src_pix = in + (width * src_line); + uint32_t* dst_pix = out + (width * dst_line); + std::copy(src_pix, src_pix + width - voffset, dst_pix + voffset); + std::copy(src_pix + width - voffset, src_pix + width, dst_pix); + } + } } private: double hsync; + double vsync; }; diff --git a/src/filter/ntsc/CMakeLists.txt b/src/filter/ntsc/CMakeLists.txt new file mode 100644 index 0000000..ab5ff94 --- /dev/null +++ b/src/filter/ntsc/CMakeLists.txt @@ -0,0 +1,11 @@ +set (SOURCES crt_core.c crt_ntsc.c ntsc-effect.c) +set (TARGET ntsc) + +if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_DEF}) +endif (MSVC) + +add_library (${TARGET} MODULE ${SOURCES}) +set_target_properties (${TARGET} PROPERTIES PREFIX "") + +install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) \ No newline at end of file diff --git a/src/filter/ntsc/crt_core.c b/src/filter/ntsc/crt_core.c new file mode 100644 index 0000000..1202d38 --- /dev/null +++ b/src/filter/ntsc/crt_core.c @@ -0,0 +1,577 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#include "crt_core.h" + +#include +#include + +/* ensure negative values for x get properly modulo'd */ +#define POSMOD(x, n) (((x) % (n) + (n)) % (n)) + +static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ + 0x0000, + 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, + 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, + 0x7f60 +}; + +static int +sintabil8(int n) +{ + int f, i, a, b; + + /* looks scary but if you don't change T14_2PI + * it won't cause out of bounds memory reads + */ + f = n >> 0 & 0xff; + i = n >> 8 & 0xff; + a = sigpsin15[i]; + b = sigpsin15[i + 1]; + return (a + ((b - a) * f >> 8)); +} + +/* 14-bit interpolated sine/cosine */ +extern void +crt_sincos14(int *s, int *c, int n) +{ + int h; + + n &= T14_MASK; + h = n & ((T14_2PI >> 1) - 1); + + if (h > ((T14_2PI >> 2) - 1)) { + *c = -sintabil8(h - (T14_2PI >> 2)); + *s = sintabil8((T14_2PI >> 1) - h); + } else { + *c = sintabil8((T14_2PI >> 2) - h); + *s = sintabil8(h); + } + if (n > ((T14_2PI >> 1) - 1)) { + *c = -*c; + *s = -*s; + } +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +/* convolution is much faster but the EQ looks softer, more authentic, and more analog */ +#define USE_CONVOLUTION 0 +#define USE_7_SAMPLE_KERNEL 1 +#define USE_6_SAMPLE_KERNEL 0 +#define USE_5_SAMPLE_KERNEL 0 + +#if (CRT_CC_SAMPLES != 4) +/* the current convolutions do not filter properly at > 4 samples */ +#undef USE_CONVOLUTION +#define USE_CONVOLUTION 0 +#endif + +#if USE_CONVOLUTION + +/* NOT 3 band equalizer, faster convolution instead. + * eq function names preserved to keep code clean + */ +static struct EQF { + int h[7]; +} eqY, eqI, eqQ; + +/* params unused to keep the function the same */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + memset(f, 0, sizeof(struct EQF)); +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i; + int *h = f->h; + + for (i = 6; i > 0; i--) { + h[i] = h[i - 1]; + } + h[0] = s; +#if USE_7_SAMPLE_KERNEL + /* index : 0 1 2 3 4 5 6 */ + /* weight: 1 4 7 8 7 4 1 */ + return (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; +#elif USE_6_SAMPLE_KERNEL + /* index : 0 1 2 3 4 5 */ + /* weight: 1 3 4 4 3 1 */ + return (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; +#elif USE_5_SAMPLE_KERNEL + /* index : 0 1 2 3 4 */ + /* weight: 1 2 2 2 1 */ + return (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; +#else + /* index : 0 1 2 3 */ + /* weight: 1 1 1 1*/ + return (s + h[3] + h[1] + h[2]) >> 2; +#endif +} + +#else + +#define HISTLEN 3 +#define HISTOLD (HISTLEN - 1) /* oldest entry */ +#define HISTNEW 0 /* newest entry */ + +#define EQ_P 16 /* if changed, the gains will need to be adjusted */ +#define EQ_R (1 << (EQ_P - 1)) /* rounding */ +/* three band equalizer */ +static struct EQF { + int lf, hf; /* fractions */ + int g[3]; /* gains */ + int fL[4]; + int fH[4]; + int h[HISTLEN]; /* history */ +} eqY, eqI, eqQ; + +/* f_lo - low cutoff frequency + * f_hi - high cutoff frequency + * rate - sampling rate + * g_lo, g_mid, g_hi - gains + */ +static void +init_eq(struct EQF *f, + int f_lo, int f_hi, int rate, + int g_lo, int g_mid, int g_hi) +{ + int sn, cs; + + memset(f, 0, sizeof(struct EQF)); + + f->g[0] = g_lo; + f->g[1] = g_mid; + f->g[2] = g_hi; + + crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); +#if (EQ_P >= 15) + f->lf = 2 * (sn << (EQ_P - 15)); +#else + f->lf = 2 * (sn >> (15 - EQ_P)); +#endif + crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); +#if (EQ_P >= 15) + f->hf = 2 * (sn << (EQ_P - 15)); +#else + f->hf = 2 * (sn >> (15 - EQ_P)); +#endif +} + +static void +reset_eq(struct EQF *f) +{ + memset(f->fL, 0, sizeof(f->fL)); + memset(f->fH, 0, sizeof(f->fH)); + memset(f->h, 0, sizeof(f->h)); +} + +static int +eqf(struct EQF *f, int s) +{ + int i, r[3]; + + f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; + f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; + + for (i = 1; i < 4; i++) { + f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; + f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; + } + + r[0] = f->fL[3]; + r[1] = f->fH[3] - f->fL[3]; + r[2] = f->h[HISTOLD] - f->fH[3]; + + for (i = 0; i < 3; i++) { + r[i] = (r[i] * f->g[i]) >> EQ_P; + } + + for (i = HISTOLD; i > 0; i--) { + f->h[i] = f->h[i - 1]; + } + f->h[HISTNEW] = s; + + return (r[0] + r[1] + r[2]); +} + +#endif + +/*****************************************************************************/ +/***************************** PUBLIC FUNCTIONS ******************************/ +/*****************************************************************************/ + +extern void +crt_resize(struct CRT *v, int w, int h, unsigned char *out) +{ + v->outw = w; + v->outh = h; + v->out = out; +} + +extern void +crt_reset(struct CRT *v) +{ + v->hue = 0; + v->saturation = 10; + v->brightness = 0; + v->contrast = 180; + v->black_point = 0; + v->white_point = 100; + v->hsync = 0; + v->vsync = 0; + + v->scanlines = 0; /* leave gaps between lines if necessary */ + v->blend = 0; /* blend new field onto previous image */ + + // these options were previously #defined in crt_core.h + v->crt_do_vsync = 1; + v->crt_do_hsync = 1; + v->do_vhs_noise = 0; +} + +extern void +crt_init(struct CRT *v, int w, int h, unsigned char *out) +{ + memset(v, 0, sizeof(struct CRT)); + crt_resize(v, w, h, out); + crt_reset(v); + v->rn = 194; + + /* kilohertz to line sample conversion */ +#define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) + + /* band gains are pre-scaled as 16-bit fixed point + * if you change the EQ_P define, you'll need to update these gains too + */ +#if (CRT_CC_SAMPLES == 4) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#elif (CRT_CC_SAMPLES == 5) + init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 12192, 7775); + init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); + init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); +#else +#error "NTSC-CRT currently only supports 4 or 5 samples per chroma period." +#endif + +} + +extern void +crt_demodulate(struct CRT *v, int noise) +{ + /* made static so all this data does not go on the stack */ + static struct { + int y, i, q; + } out[AV_LEN + 1], *yiqA, *yiqB; + int i, j, line, rn; + signed char *sig; + int s = 0; + int field, ratio; + int *ccr; /* color carrier signal */ + int huesn, huecs; + int xnudge = -3, ynudge = 3; + int bright = v->brightness - (BLACK_LEVEL + v->black_point); + int pitch; + + pitch = v->outw * BPP; + + crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); + huesn >>= 11; /* make 4-bit */ + huecs >>= 11; + + rn = v->rn; + if(!v->crt_do_vsync) + { + /* determine field before we add noise, + * otherwise it's not reliably recoverable + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->analog + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto found_field; + } + } + } + found_field: + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); + v->vsync = -3; + } + if(v->do_vhs_noise) + { + line = ((rand() % 8) - 4) + 14; + } + for (i = 0; i < CRT_INPUT_SIZE; i++) { + int nn = noise; + if(v->do_vhs_noise) + { + rn = rand(); + if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) && + i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) { + int ln, sn, cs; + + ln = (i * line) / CRT_HRES; + crt_sincos14(&sn, &cs, ln * 8192 / 180); + nn = cs >> 8; + } + } + else + { + rn = (214019 * rn + 140327895); + } + /* signal + noise */ + s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8); + if (s > 127) { s = 127; } + if (s < -127) { s = -127; } + v->inp[i] = s; + } + v->rn = rn; + + if(v->crt_do_vsync) + { + /* Look for vertical sync. + * + * This is done by integrating the signal and + * seeing if it exceeds a threshold. The threshold of + * the vertical sync pulse is much higher because the + * vsync pulse is a lot longer than the hsync pulse. + * The signal needs to be integrated to lessen + * the noise in the signal. + */ + for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { + line = POSMOD(v->vsync + i, CRT_VRES); + sig = v->inp + line * CRT_HRES; + s = 0; + for (j = 0; j < CRT_HRES; j++) { + s += sig[j]; + /* increase the multiplier to make the vsync + * more stable when there is a lot of noise + */ + if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { + goto vsync_found; + } + } + } + vsync_found: + v->vsync = line; /* vsync found (or gave up) at this line */ + /* if vsync signal was in second half of line, odd field */ + field = (j > (CRT_HRES / 2)); + } + + /* ratio of output height to active video lines in the signal */ + ratio = (v->outh << 16) / CRT_LINES; + ratio = (ratio + 32768) >> 16; + + field = (field * (ratio / 2)); + + for (line = CRT_TOP; line < CRT_BOT; line++) { + unsigned pos, ln, scanR; + int scanL, dx; + int L, R; + unsigned char *cL, *cR; +#if (CRT_CC_SAMPLES == 4) + int wave[CRT_CC_SAMPLES]; +#else + int waveI[CRT_CC_SAMPLES]; + int waveQ[CRT_CC_SAMPLES]; +#endif + int dci, dcq; /* decoded I, Q */ + int xpos, ypos; + int beg, end; + int phasealign; + + beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field; + end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field; + + if (beg >= v->outh) { continue; } + if (end > v->outh) { end = v->outh; } + + /* Look for horizontal sync. + * See comment above regarding vertical sync. + */ + ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; + sig = v->inp + ln + v->hsync; + s = 0; + for (i = -CRT_HSYNC_WINDOW; i < CRT_HSYNC_WINDOW; i++) { + s += sig[SYNC_BEG + i]; + if (s <= (CRT_HSYNC_THRESH * SYNC_LEVEL)) { + break; + } + } + if(v->crt_do_hsync) + { + v->hsync = POSMOD(i + v->hsync, CRT_HRES); + } + else + { + v->hsync = 0; + } + + xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); + ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); + pos = xpos + ypos * CRT_HRES; + + ccr = v->ccf[ypos % CRT_CC_VPER]; +#if (CRT_CC_SAMPLES == 4) + sig = v->inp + ln + (v->hsync & ~3); /* faster */ +#else + sig = v->inp + ln + (v->hsync - (v->hsync % CRT_CC_SAMPLES)); +#endif + for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { + int p, n; + p = ccr[i % CRT_CC_SAMPLES] * 127 / 128; /* fraction of the previous */ + n = sig[i]; /* mixed with the new sample */ + ccr[i % CRT_CC_SAMPLES] = p + n; + } + + phasealign = POSMOD(v->hsync, CRT_CC_SAMPLES); + +#if (CRT_CC_SAMPLES == 4) + /* amplitude of carrier = saturation, phase difference = hue */ + dci = ccr[(phasealign + 1) & 3] - ccr[(phasealign + 3) & 3]; + dcq = ccr[(phasealign + 2) & 3] - ccr[(phasealign + 0) & 3]; + + wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; + wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; + wave[2] = -wave[0]; + wave[3] = -wave[1]; +#elif (CRT_CC_SAMPLES == 5) + { + int dciA, dciB; + int dcqA, dcqB; + int ang = (v->hue % 360); + int off180 = CRT_CC_SAMPLES / 2; + int off90 = CRT_CC_SAMPLES / 4; + int peakA = phasealign + off90; + int peakB = phasealign + 0; + dciA = dciB = dcqA = dcqB = 0; + /* amplitude of carrier = saturation, phase difference = hue */ + dciA = ccr[(peakA) % CRT_CC_SAMPLES]; + /* average */ + dciB = (ccr[(peakA + off180) % CRT_CC_SAMPLES] + + ccr[(peakA + off180 + 1) % CRT_CC_SAMPLES]) / 2; + dcqA = ccr[(peakB + off180) % CRT_CC_SAMPLES]; + dcqB = ccr[(peakB) % CRT_CC_SAMPLES]; + dci = dciA - dciB; + dcq = dcqA - dcqB; + /* create wave tables and rotate them by the hue adjustment angle */ + for (i = 0; i < CRT_CC_SAMPLES; i++) { + int sn, cs; + crt_sincos14(&sn, &cs, ang * 8192 / 180); + waveI[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + /* Q is offset by 90 */ + crt_sincos14(&sn, &cs, (ang + 90) * 8192 / 180); + waveQ[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; + ang += (360 / CRT_CC_SAMPLES); + } + } +#endif + sig = v->inp + pos; + + dx = ((AV_LEN - 1) << 12) / v->outw; + scanL = 0; + scanR = (AV_LEN - 1) << 12; + L = 0; + R = AV_LEN; + + reset_eq(&eqY); + reset_eq(&eqI); + reset_eq(&eqQ); + +#if (CRT_CC_SAMPLES == 4) + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; + } +#else + for (i = L; i < R; i++) { + out[i].y = eqf(&eqY, sig[i] + bright) << 4; + out[i].i = eqf(&eqI, sig[i] * waveI[i % CRT_CC_SAMPLES] >> 9) >> 3; + out[i].q = eqf(&eqQ, sig[i] * waveQ[i % CRT_CC_SAMPLES] >> 9) >> 3; + } +#endif + + cL = v->out + (beg * pitch); + cR = cL + pitch; + + for (pos = scanL; pos < scanR && cL < cR; pos += dx) { + int y, i, q; + int r, g, b; + int aa, bb; + + R = pos & 0xfff; + L = 0xfff - R; + s = pos >> 12; + + yiqA = out + s; + yiqB = out + s + 1; + + /* interpolate between samples if needed */ + y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); + i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); + q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); + + /* YIQ to RGB */ + r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; + g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; + b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + if (v->blend) { + aa = (r << 16 | g << 8 | b); + bb = cL[0] << 16 | cL[1] << 8 | cL[2]; + + + /* blend with previous color there */ + bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); + } else { + bb = (r << 16 | g << 8 | b); + } + + cL[0] = bb >> 16 & 0xff; + cL[1] = bb >> 8 & 0xff; + cL[2] = bb >> 0 & 0xff; + cL[3] = 0xff; + + cL += BPP; + } + + /* duplicate extra lines */ + for (s = beg + 1; s < (end - v->scanlines); s++) { + memcpy(v->out + s * pitch, v->out + (s - 1) * pitch, pitch); + } + } +} diff --git a/src/filter/ntsc/crt_core.h b/src/filter/ntsc/crt_core.h new file mode 100644 index 0000000..eed9332 --- /dev/null +++ b/src/filter/ntsc/crt_core.h @@ -0,0 +1,101 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#ifndef _CRT_CORE_H_ +#define _CRT_CORE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* crt_core.h + * + * The demodulator. This is also where you can define which system to emulate. + * + */ + +/* library version */ +#define CRT_MAJOR 2 +#define CRT_MINOR 3 +#define CRT_PATCH 2 + +#include "crt_ntsc.h" + +// frei0r always uses 4 bits per pixel so it is pointless for there to be any other possibilities +#define BPP 4 + +struct CRT { + signed char analog[CRT_INPUT_SIZE]; + signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ + + int outw, outh; /* output width/height */ + unsigned char *out; /* output image */ + + int hue, brightness, contrast, saturation; /* common monitor settings */ + int black_point, white_point; /* user-adjustable */ + int scanlines; /* leave gaps between lines if necessary */ + int blend; /* blend new field onto previous image */ + unsigned v_fac; /* factor to stretch img vertically onto the output img */ + + // these options were previously #defined in crt_core.h + int crt_do_vsync; + int crt_do_hsync; + int do_vhs_noise; /* 0 = no additional vhs noise, 1 = with additional vhs noise */ + + /* internal data */ + int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */ + int hsync, vsync; /* keep track of sync over frames */ + int rn; /* seed for the 'random' noise */ +}; + +/* Initializes the library. Sets up filters. + * w - width of the output image + * h - height of the output image + * f - format of the output image + * out - pointer to output image data + */ +extern void crt_init(struct CRT *v, int w, int h, unsigned char *out); + +/* Updates the output image parameters + * w - width of the output image + * h - height of the output image + * f - format of the output image + * out - pointer to output image data + */ +extern void crt_resize(struct CRT *v, int w, int h, unsigned char *out); + +/* Resets the CRT settings back to their defaults */ +extern void crt_reset(struct CRT *v); + +/* Modulates RGB image into an analog NTSC signal + * s - struct containing settings to apply to this field + */ +extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s); + +/* Demodulates the NTSC signal generated by crt_modulate() + * noise - the amount of noise added to the signal (0 - inf) + */ +extern void crt_demodulate(struct CRT *v, int noise); + +/*****************************************************************************/ +/*************************** FIXED POINT SIN/COS *****************************/ +/*****************************************************************************/ + +#define T14_2PI 16384 +#define T14_MASK (T14_2PI - 1) +#define T14_PI (T14_2PI / 2) + +extern void crt_sincos14(int *s, int *c, int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/filter/ntsc/crt_ntsc.c b/src/filter/ntsc/crt_ntsc.c new file mode 100644 index 0000000..7933b73 --- /dev/null +++ b/src/filter/ntsc/crt_ntsc.c @@ -0,0 +1,333 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ + +#include "crt_core.h" + +#if (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) +#include +#include + +#if (CRT_CHROMA_PATTERN == 1) +/* 227.5 subcarrier cycles per line means every other line has reversed phase */ +#define CC_PHASE(ln) (((ln) & 1) ? -1 : 1) +#else +#define CC_PHASE(ln) (1) +#endif + +#define EXP_P 11 +#define EXP_ONE (1 << EXP_P) +#define EXP_MASK (EXP_ONE - 1) +#define EXP_PI 6434 +#define EXP_MUL(x, y) (((x) * (y)) >> EXP_P) +#define EXP_DIV(x, y) (((x) << EXP_P) / (y)) + +static int e11[] = { + EXP_ONE, + 5567, /* e */ + 15133, /* e^2 */ + 41135, /* e^3 */ + 111817 /* e^4 */ +}; + +/* fixed point e^x */ +static int +expx(int n) +{ + int neg, idx, res; + int nxt, acc, del; + int i; + + if (n == 0) { + return EXP_ONE; + } + neg = n < 0; + if (neg) { + n = -n; + } + idx = n >> EXP_P; + res = EXP_ONE; + for (i = 0; i < idx / 4; i++) { + res = EXP_MUL(res, e11[4]); + } + idx &= 3; + if (idx > 0) { + res = EXP_MUL(res, e11[idx]); + } + + n &= EXP_MASK; + nxt = EXP_ONE; + acc = 0; + del = 1; + for (i = 1; i < 17; i++) { + acc += nxt / del; + nxt = EXP_MUL(nxt, n); + del *= i; + if (del > nxt || nxt <= 0 || del <= 0) { + break; + } + } + res = EXP_MUL(res, acc); + + if (neg) { + res = EXP_DIV(EXP_ONE, res); + } + return res; +} + +/*****************************************************************************/ +/********************************* FILTERS ***********************************/ +/*****************************************************************************/ + +/* infinite impulse response low pass filter for bandlimiting YIQ */ +static struct IIRLP { + int c; + int h; /* history */ +} iirY, iirI, iirQ; + +/* freq - total bandwidth + * limit - max frequency + */ +static void +init_iir(struct IIRLP *f, int freq, int limit) +{ + int rate; /* cycles/pixel rate */ + + memset(f, 0, sizeof(struct IIRLP)); + rate = (freq << 9) / limit; + f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); +} + +static void +reset_iir(struct IIRLP *f) +{ + f->h = 0; +} + +/* hi-pass for debugging */ +#define HIPASS 0 + +static int +iirf(struct IIRLP *f, int s) +{ + f->h += EXP_MUL(s - f->h, f->c); +#if HIPASS + return s - f->h; +#else + return f->h; +#endif +} + +extern void +crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) +{ + int x, y, xo, yo; + int destw = AV_LEN; + int desth = ((CRT_LINES * 64500) >> 16); + int iccf[CRT_CC_SAMPLES]; + int ccmodI[CRT_CC_SAMPLES]; /* color phase for mod */ + int ccmodQ[CRT_CC_SAMPLES]; /* color phase for mod */ + int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */ + int sn, cs, n, ph; + int inv_phase = 0; + int aberration = 0; + + if (!s->iirs_initialized) { + int y_freq = Y_FREQ_OFF; + int i_freq = I_FREQ_OFF; + int q_freq = Q_FREQ_OFF; + + switch(s->vhs_mode) + { + case VHS_OFF: + break; + case VHS_SP: + y_freq = Y_FREQ_SP; + i_freq = I_FREQ_SP; + q_freq = Q_FREQ_SP; + break; + case VHS_LP: + y_freq = Y_FREQ_LP; + i_freq = I_FREQ_LP; + q_freq = Q_FREQ_LP; + break; + case VHS_EP: + y_freq = Y_FREQ_EP; + i_freq = I_FREQ_EP; + q_freq = Q_FREQ_EP; + break; + default: + break; + } + + init_iir(&iirY, L_FREQ, y_freq); + init_iir(&iirI, L_FREQ, i_freq); + init_iir(&iirQ, L_FREQ, q_freq); + s->iirs_initialized = 1; + } + if (s->raw) { + destw = s->w; + desth = s->h; + if (destw > AV_LEN) { + destw = AV_LEN; + } + if (desth > ((CRT_LINES * 64500) >> 16)) { + desth = ((CRT_LINES * 64500) >> 16); + } + } + + if (s->as_color) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + n = s->hue + x * (360 / CRT_CC_SAMPLES); + crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); + ccburst[x] = sn >> 10; + crt_sincos14(&sn, &cs, n * 8192 / 180); + ccmodI[x] = sn >> 10; + crt_sincos14(&sn, &cs, (n - 90) * 8192 / 180); + ccmodQ[x] = sn >> 10; + } + } else { + memset(ccburst, 0, sizeof(ccburst)); + memset(ccmodI, 0, sizeof(ccmodI)); + memset(ccmodQ, 0, sizeof(ccmodQ)); + } + + xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2; + yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2; + + s->field &= 1; + s->frame &= 1; + inv_phase = (s->field == s->frame); + ph = CC_PHASE(inv_phase); + + /* align signal */ + xo = (xo & ~3); + if (s->do_aberration) { + aberration = ((rand() % 12) - 8) + 14; + } + for (n = 0; n < CRT_VRES; n++) { + int t; /* time */ + signed char *line = &v->analog[n * CRT_HRES]; + + t = LINE_BEG; + + if (n <= 3 || (n >= 7 && n <= 9)) { + /* equalizing pulses - small blips of sync, mostly blank */ + while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else if (n >= 4 && n <= 6) { + int even[4] = { 46, 50, 96, 100 }; + int odd[4] = { 4, 50, 96, 100 }; + int *offs = even; + if (s->field == 1) { + offs = odd; + } + /* vertical sync pulse - small blips of blank, mostly sync */ + while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; + while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; + } else { + int cb; + if (n < (CRT_VRES - aberration)) { + /* video line */ + while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ + while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ + } + while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ + + if (n < CRT_TOP) { + while (t < CRT_HRES) line[t++] = BLANK_LEVEL; + } + + /* CB_CYCLES of color burst at 3.579545 Mhz */ + for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { +#if (CRT_CHROMA_PATTERN == 1) + int off180 = CRT_CC_SAMPLES / 2; + cb = ccburst[(t + inv_phase * off180) % CRT_CC_SAMPLES]; +#else + cb = ccburst[t % CRT_CC_SAMPLES]; +#endif + line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; + iccf[t % CRT_CC_SAMPLES] = line[t]; + } + } + } + + if(s->vhs_mode != VHS_OFF) + { + /* reset hsync every frame so only the bottom part is warped */ + v->hsync = 0; + } + + for (y = 0; y < desth; y++) { + int field_offset; + int sy; + + field_offset = (s->field * s->h + desth) / desth / 2; + sy = (y * s->h) / desth; + + sy += field_offset; + + if (sy >= s->h) sy = s->h; + + sy *= s->w; + + reset_iir(&iirY); + reset_iir(&iirI); + reset_iir(&iirQ); + + for (x = 0; x < destw; x++) { + int fy, fi, fq; + int rA, gA, bA; + const unsigned char *pix; + int ire; /* composite signal */ + int xoff; + + pix = s->data + ((((x * s->w) / destw) + sy) * BPP); + rA = pix[0]; + gA = pix[1]; + bA = pix[2]; + + + /* RGB to YIQ */ + fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; + fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; + fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; + ire = BLACK_LEVEL + v->black_point; + + xoff = (x + xo) % CRT_CC_SAMPLES; + /* bandlimit Y,I,Q */ + fy = iirf(&iirY, fy); + fi = iirf(&iirI, fi) * ph * ccmodI[xoff] >> 4; + fq = iirf(&iirQ, fq) * ph * ccmodQ[xoff] >> 4; + ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; + if (ire < 0) ire = 0; + if (ire > 110) ire = 110; + + v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; + } + } + for (n = 0; n < CRT_CC_VPER; n++) { + for (x = 0; x < CRT_CC_SAMPLES; x++) { + if(s->vhs_mode != VHS_OFF) + { + v->ccf[n][x] = 0; + } + else + { + v->ccf[n][x] = iccf[x] << 7; + } + } + } +} +#endif diff --git a/src/filter/ntsc/crt_ntsc.h b/src/filter/ntsc/crt_ntsc.h new file mode 100644 index 0000000..a979ff9 --- /dev/null +++ b/src/filter/ntsc/crt_ntsc.h @@ -0,0 +1,159 @@ +/*****************************************************************************/ +/* + * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation + * + * by EMMIR 2018-2023 + * + * YouTube: https://www.youtube.com/@EMMIR_KC/videos + * Discord: https://discord.com/invite/hdYctSmyQJ + */ +/*****************************************************************************/ +#ifndef _CRT_NTSC_VHS_H_ +#define _CRT_NTSC_VHS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* crt_ntscvhs.h + * + * An interface to convert a digital image to an analog NTSC signal with VHS + * quality and some optional signal aberration at the bottom. + * + */ +/* 0 = vertical chroma (228 chroma clocks per line) */ +/* 1 = checkered chroma (227.5 chroma clocks per line) */ +#define CRT_CHROMA_PATTERN 1 + +/* chroma clocks (subcarrier cycles) per line */ +#if (CRT_CHROMA_PATTERN == 1) +#define CRT_CC_LINE 2275 +#else +/* this will give the 'rainbow' effect in the famous waterfall scene */ +#define CRT_CC_LINE 2280 +#endif + +/* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ +#define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ +#define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ +#define CRT_VRES 262 /* vertical resolution */ +#define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) + +#define CRT_TOP 21 /* first line with active video */ +#define CRT_BOT 261 /* final line with active video */ +#define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ + +#define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */ +#define CRT_CC_VPER 1 /* vertical period in which the artifacts repeat */ + +/* search windows, in samples */ +#define CRT_HSYNC_WINDOW 8 +#define CRT_VSYNC_WINDOW 8 + +/* accumulated signal threshold required for sync detection. + * Larger = more stable, until it's so large that it is never reached in which + * case the CRT won't be able to sync + */ +#define CRT_HSYNC_THRESH 4 +#define CRT_VSYNC_THRESH 94 + +/* + * FULL HORIZONTAL LINE SIGNAL (~63500 ns) + * |---------------------------------------------------------------------------| + * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) + * |-------------------||------------------------------------------------------| + * + * + * WITHIN HBLANK PERIOD: + * + * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) + * |--------------||---------------||------------||-------------||-------------| + * BLANK SYNC BLANK BLANK BLANK + * + */ +#define LINE_BEG 0 +#define FP_ns 1500 /* front porch */ +#define SYNC_ns 4700 /* sync tip */ +#define BW_ns 600 /* breezeway */ +#define CB_ns 2500 /* color burst */ +#define BP_ns 1600 /* back porch */ +#define AV_ns 52600 /* active video */ +#define HB_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns) /* h blank */ +/* line duration should be ~63500 ns */ +#define LINE_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns + AV_ns) + +/* convert nanosecond offset to its corresponding point on the sampled line */ +#define ns2pos(ns) ((ns) * CRT_HRES / LINE_ns) +/* starting points for all the different pulses */ +#define FP_BEG ns2pos(0) +#define SYNC_BEG ns2pos(FP_ns) +#define BW_BEG ns2pos(FP_ns + SYNC_ns) +#define CB_BEG ns2pos(FP_ns + SYNC_ns + BW_ns) +#define BP_BEG ns2pos(FP_ns + SYNC_ns + BW_ns + CB_ns) +#define AV_BEG ns2pos(HB_ns) +#define AV_LEN ns2pos(AV_ns) + +/* somewhere between 7 and 12 cycles */ +#define CB_CYCLES 10 + +#define VHS_OFF 0 +#define VHS_SP 1 +#define VHS_LP 2 +#define VHS_EP 3 + +#define VHS_MODE VHS_OFF + +/* frequencies for bandlimiting */ +#define L_FREQ 1431818 /* full line */ + +#define Y_FREQ_OFF 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ +#define I_FREQ_OFF 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ +#define Q_FREQ_OFF 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ + +#define Y_FREQ_SP 300000 /* Luma (Y) 3.0 MHz of the 14.31818 MHz */ +#define I_FREQ_SP 62700 /* Chroma (I) 627 kHz of the 14.31818 MHz */ +#define Q_FREQ_SP 62700 /* Chroma (Q) 627 kHz of the 14.31818 MHz */ + +#define Y_FREQ_LP 240000 /* Luma (Y) 2.4 MHz of the 14.31818 MHz */ +#define I_FREQ_LP 40000 /* Chroma (I) 400 kHz of the 14.31818 MHz */ +#define Q_FREQ_LP 40000 /* Chroma (Q) 400 kHz of the 14.31818 MHz */ + +#define Y_FREQ_EP 200000 /* Luma (Y) 2.0 MHz of the 14.31818 MHz */ +#define I_FREQ_EP 37000 /* Chroma (I) 370 kHz of the 14.31818 MHz */ +#define Q_FREQ_EP 37000 /* Chroma (Q) 370 kHz of the 14.31818 MHz */ + + +/* IRE units (100 = 1.0V, -40 = 0.0V) */ +#define WHITE_LEVEL 100 +#define BURST_LEVEL 20 +#define BLACK_LEVEL 7 +#define BLANK_LEVEL 0 +#define SYNC_LEVEL -40 + +struct NTSC_SETTINGS { + const unsigned char *data; /* image data */ + int w, h; /* width and height of image */ + int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ + int as_color; /* 0 = monochrome, 1 = full color */ + int field; /* 0 = even, 1 = odd */ + int frame; /* 0 = even, 1 = odd */ + int hue; /* 0-359 */ + int xoffset; /* x offset in sample space. 0 is minimum value */ + int yoffset; /* y offset in # of lines. 0 is minimum value */ + int do_aberration; /* 0 = no aberration, 1 = with aberration */ + + // these are changed by the vhs mode + int vhs_mode; + int y_freq; + int i_freq; + int q_freq; + + /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ + int iirs_initialized; /* internal state */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/filter/ntsc/ntsc-effect.c b/src/filter/ntsc/ntsc-effect.c new file mode 100644 index 0000000..e2b0c3b --- /dev/null +++ b/src/filter/ntsc/ntsc-effect.c @@ -0,0 +1,237 @@ +#include + +#include "frei0r.h" +#include "frei0r/math.h" + +#include "crt_core.h" + +// the actual NTSC emulation code is modified from here: https://github.com/LMP88959/NTSC-CRT + +typedef struct ntsc_instance +{ + // image dimensions + int width; + int height; + + // parameters + struct CRT crt; + struct NTSC_SETTINGS ntsc_settings; + + int noise; + double vhs_speed; + + int field; + int progressive; + +} ntsc_instance_t; + +// these functions are for frei0r +// mostly copy/paste/slightly modified from the other frei0r effects +int f0r_init() +{ + return 1; +} + +void f0r_deinit() +{} + +void f0r_get_plugin_info(f0r_plugin_info_t* info) +{ + info->name = "NTSC"; + info->author = "EMMIR, esmane"; + info->explanation = "Simulates NTSC analog video."; + info->plugin_type = F0R_PLUGIN_TYPE_FILTER; + info->color_model = F0R_COLOR_MODEL_RGBA8888; + info->frei0r_version = FREI0R_MAJOR_VERSION; + info->major_version = 0; + info->minor_version = 1; + info->num_params = 8; +} + +void f0r_get_param_info(f0r_param_info_t* info, int param_index) +{ + switch(param_index) + { + case 0: + info->name = "Signal Noise"; + info->explanation = "Amount of noise introduced into the NTSC signal."; + info->type = F0R_PARAM_DOUBLE; + break; + + case 1: + info->name = "VHS Speed"; + info->explanation = "Simulates VHS. 0 = off, 1 = SP, 2 = LP, 3 = EP"; + info->type = F0R_PARAM_DOUBLE; + break; + + case 2: + info->name = "VHS Noise"; + info->explanation = "Toggles VHS noise at the bottom of the screen"; + info->type = F0R_PARAM_BOOL; + break; + + case 3: + info->name = "Aberration"; + info->explanation = "Toggles VHS aberration at the bottom of the screen. Not visible if V-sync is on."; + info->type = F0R_PARAM_BOOL; + break; + + case 4: + info->name = "Force V-sync"; + info->explanation = "Forces V-sync even when the signal is really noisy."; + info->type = F0R_PARAM_BOOL; + break; + + case 5: + info->name = "Force H-sync"; + info->explanation = "Forces V-sync even when the signal is really noisy."; + info->type = F0R_PARAM_BOOL; + break; + + case 6: + info->name = "Progressive Scan"; + info->explanation = "Toggles progressive scan (Interlaced if off)."; + info->type = F0R_PARAM_BOOL; + break; + + case 7: + info->name = "Blend"; + info->explanation = "Blends frames."; + info->type = F0R_PARAM_BOOL; + break; + } +} + +f0r_instance_t f0r_construct(unsigned int width, unsigned int height) +{ + ntsc_instance_t* inst = (ntsc_instance_t*)calloc(1, sizeof(*inst)); + + inst->width = width; + inst->height = height; + + inst->vhs_speed = 0.0; + inst->field = 0; + inst->progressive = 1; + + crt_init(&(inst->crt), width, height, NULL); + + // init NTSC_SETTINGS + inst->ntsc_settings.data = NULL; + inst->ntsc_settings.w = width; + inst->ntsc_settings.h = height; + inst->ntsc_settings.raw = 0; + inst->ntsc_settings.as_color = 1; + inst->ntsc_settings.field = 0; + inst->ntsc_settings.frame = 0; + inst->ntsc_settings.hue = 0; + inst->ntsc_settings.xoffset = 0; + inst->ntsc_settings.yoffset = 0; + inst->ntsc_settings.do_aberration = 0; + + // these are changed by the vhs mode + inst->ntsc_settings.vhs_mode = 0; + inst->ntsc_settings.y_freq = 0; + inst->ntsc_settings.i_freq = 0; + inst->ntsc_settings.q_freq = 0; + + /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ + inst->ntsc_settings.iirs_initialized = 0; + + inst->noise = 0; + + return (f0r_instance_t)inst; +} + +void f0r_destruct(f0r_instance_t instance) +{ + ntsc_instance_t* inst = (ntsc_instance_t*)instance; + free(instance); +} + + +void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) +{ + ntsc_instance_t* inst = (ntsc_instance_t*)instance; + switch(param_index) + { + case 0: + inst->noise = *((double*)param) * 100; + break; + case 1: + inst->ntsc_settings.vhs_mode = (int)CLAMP(*((double*)param) * 4, 0, 4); + inst->ntsc_settings.iirs_initialized = 0; + break; + case 2: + inst->crt.do_vhs_noise = (*((double*)param) >= 0.5); + break; + case 3: + inst->ntsc_settings.do_aberration = (*((double*)param) >= 0.5); + break; + case 4: + inst->crt.crt_do_vsync = !(*((double*)param) >= 0.5); + break; + case 5: + inst->crt.crt_do_hsync = !(*((double*)param) >= 0.5); + break; + case 6: + inst->progressive = (*((double*)param) >= 0.5); + break; + case 7: + inst->crt.blend = (*((double*)param) >= 0.5); + break; + } +} + +void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) +{ + ntsc_instance_t* inst = (ntsc_instance_t*)instance; + switch(param_index) + { + case 0: + *((double*)param) = (inst->noise / 100); + break; + case 1: + *((double*)param) = (inst->ntsc_settings.vhs_mode / 4); + break; + case 2: + *((double*)param) = (inst->crt.do_vhs_noise ? 1.0 : 0.0); + break; + case 3: + *((double*)param) = (inst->ntsc_settings.do_aberration ? 1.0 : 0.0); + break; + case 4: + *((double*)param) = !(inst->crt.crt_do_vsync ? 1.0 : 0.0); + break; + case 5: + *((double*)param) = !(inst->crt.crt_do_hsync ? 1.0 : 0.0); + break; + case 6: + *((double*)param) = (inst->progressive ? 1.0 : 0.0); + break; + case 7: + *((double*)param) = (inst->crt.blend ? 1.0 : 0.0); + break; + } +} + + +void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe) +{ + ntsc_instance_t* inst = (ntsc_instance_t*)instance; + + inst->crt.out = (char*)outframe; + inst->ntsc_settings.data = (const char*)inframe; + + inst->ntsc_settings.field = inst->field & 1; + if (inst->ntsc_settings.field == 0) { + /* a frame is two fields */ + inst->ntsc_settings.frame ^= 1; + } + + crt_modulate(&(inst->crt), &(inst->ntsc_settings)); + crt_demodulate(&(inst->crt), inst->noise); + if(!inst->progressive) + { + inst->field ^= 1; + } +} diff --git a/src/filter/pixeliz0r/pixeliz0r.c b/src/filter/pixeliz0r/pixeliz0r.c index c1d9d30..1779332 100644 --- a/src/filter/pixeliz0r/pixeliz0r.c +++ b/src/filter/pixeliz0r/pixeliz0r.c @@ -1,12 +1,12 @@ #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #include #include #include static uint32_t average(const uint32_t* start, - int bxsize, - int bysize, int xsize); + int bxsize, + int bysize, int xsize); static void fill_block(uint32_t* start, int bxsize, @@ -16,15 +16,16 @@ typedef struct pixelizer_instance { - unsigned int width; - unsigned int height; - unsigned int block_size_x; - unsigned int block_size_y; + unsigned int width; + unsigned int height; + unsigned int block_size_x; + unsigned int block_size_y; + unsigned char pass_alpha; } pixelizer_instance_t; int f0r_init() { - return 1; + return 1; } void f0r_deinit() @@ -32,196 +33,213 @@ void f0r_get_plugin_info(f0r_plugin_info_t* pixelizerInfo) { - pixelizerInfo->name = "pixeliz0r"; - pixelizerInfo->author = "Gephex crew"; - pixelizerInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; - pixelizerInfo->color_model = F0R_COLOR_MODEL_PACKED32; - pixelizerInfo->frei0r_version = FREI0R_MAJOR_VERSION; - pixelizerInfo->major_version = 1; - pixelizerInfo->minor_version = 0; - pixelizerInfo->num_params = 2; - pixelizerInfo->explanation = "Pixelize input image."; + pixelizerInfo->name = "pixeliz0r"; + pixelizerInfo->author = "Gephex crew"; + pixelizerInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; + pixelizerInfo->color_model = F0R_COLOR_MODEL_PACKED32; + pixelizerInfo->frei0r_version = FREI0R_MAJOR_VERSION; + pixelizerInfo->major_version = 2; + pixelizerInfo->minor_version = 0; + pixelizerInfo->num_params = 3; + pixelizerInfo->explanation = "Pixelize input image."; } void f0r_get_param_info(f0r_param_info_t* info, int param_index) { - switch(param_index) + switch(param_index) { case 0: - info->name = "Block width"; - info->type = F0R_PARAM_DOUBLE; - info->explanation = "Horizontal size of one 'pixel'"; - break; + info->name = "Block width"; + info->type = F0R_PARAM_DOUBLE; + info->explanation = "Horizontal size of one 'pixel'"; + break; case 1: - info->name = "Block height"; - info->type = F0R_PARAM_DOUBLE; - info->explanation = "Vertical size of one 'pixel'"; - break; + info->name = "Block height"; + info->type = F0R_PARAM_DOUBLE; + info->explanation = "Vertical size of one 'pixel'"; + break; + case 2: + info->name = "Pass-through alpha"; + info->type = F0R_PARAM_BOOL; + info->explanation = "Do not average the alpha channel over each block"; + break; } } f0r_instance_t f0r_construct(unsigned int width, unsigned int height) { - pixelizer_instance_t* inst = (pixelizer_instance_t*)calloc(1, sizeof(*inst)); - inst->width = width; inst->height = height; - inst->block_size_x = 8; inst->block_size_y = 8; - return (f0r_instance_t)inst; + pixelizer_instance_t* inst = (pixelizer_instance_t*)calloc(1, sizeof(*inst)); + inst->width = width; inst->height = height; + inst->block_size_x = 8; inst->block_size_y = 8; + return (f0r_instance_t)inst; } void f0r_destruct(f0r_instance_t instance) { - free(instance); + free(instance); } void f0r_set_param_value(f0r_instance_t instance, - f0r_param_t param, int param_index) + f0r_param_t param, int param_index) { - assert(instance); - pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; - - switch(param_index) + assert(instance); + pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; + + switch(param_index) { case 0: - // scale to [1..width] - inst->block_size_x = MAX(1 + ( *((double*)param) * (inst->width/2)), 1); - break; + // scale to [1..width] + inst->block_size_x = MAX(1 + ( *((double*)param) * (inst->width/2)), 1); + break; case 1: - // scale to [1..height] - inst->block_size_y = MAX(1 + ( *((double*)param) * (inst->height/2)), 1); - break; - } + // scale to [1..height] + inst->block_size_y = MAX(1 + ( *((double*)param) * (inst->height/2)), 1); + break; + case 2: + inst->pass_alpha = *((double*)param) >= 0.5; + break; + } } void f0r_get_param_value(f0r_instance_t instance, - f0r_param_t param, int param_index) + f0r_param_t param, int param_index) { - assert(instance); - pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; - - switch(param_index) + assert(instance); + pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; + + switch(param_index) { case 0: - // scale back to [0..1] - *((double*)param) = (double)(inst->block_size_x-1)/(inst->width/2); - break; + // scale back to [0..1] + *((double*)param) = (double)(inst->block_size_x-1)/(inst->width/2); + break; case 1: - // scale back to [0..1] - *((double*)param) = (double)(inst->block_size_y-1)/(inst->height/2); - break; - } + // scale back to [0..1] + *((double*)param) = (double)(inst->block_size_y-1)/(inst->height/2); + break; + case 2: + *((double*)param) = inst->pass_alpha ? 1.0 : 0.0; + break; + } } void f0r_update(f0r_instance_t instance, double time, - const uint32_t* inframe, uint32_t* outframe) -{ - assert(instance); - pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; - unsigned int xsize = inst->width; - unsigned int ysize = inst->height; - unsigned int bsizex = inst->block_size_x; - unsigned int bsizey = inst->block_size_y; - unsigned int offset = 0; - unsigned int blocks_x, blocks_y, xrest, yrest, xi, yi; - - - blocks_x = xsize / bsizex; - blocks_y = ysize / bsizey; - xrest = xsize - blocks_x*bsizex; - yrest = ysize - blocks_y*bsizey; - - uint32_t* dst = outframe; - const uint32_t* src = inframe; - - - if (bsizex == 1 && bsizey == 1) - { - memcpy(dst, src, xsize*ysize*sizeof(uint32_t)); - } - - - // now get average for every block and write the average color to the output - for (yi = 0; yi < blocks_y; ++yi) + const uint32_t* inframe, uint32_t* outframe) +{ + assert(instance); + pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; + unsigned int xsize = inst->width; + unsigned int ysize = inst->height; + unsigned int bsizex = inst->block_size_x; + unsigned int bsizey = inst->block_size_y; + unsigned int offset = 0; + unsigned int blocks_x, blocks_y, xrest, yrest, xi, yi; + + blocks_x = xsize / bsizex; + blocks_y = ysize / bsizey; + xrest = xsize - blocks_x*bsizex; + yrest = ysize - blocks_y*bsizey; + + uint32_t* dst = outframe; + const uint32_t* src = inframe; + + + if (bsizex == 1 && bsizey == 1) + { + memcpy(dst, src, xsize*ysize*sizeof(uint32_t)); + } + + + // now get average for every block and write the average color to the output + for (yi = 0; yi < blocks_y; ++yi) { - offset = yi*bsizey*xsize; - for (xi = 0; xi < blocks_x; ++xi) - { - uint32_t col = average(src + offset, bsizex, bsizey, xsize); - fill_block(dst + offset, bsizex, bsizey, xsize, col); - offset += bsizex; - } - if (xrest > 0) - { - uint32_t col = average(src + offset, xrest, bsizey, xsize); - fill_block(dst + offset, xrest, bsizey, xsize, col); - } - } - // check for last line - if (yrest > 0) - { - offset = blocks_y*bsizey*xsize; - for (xi = 0; xi < blocks_x; ++xi) - { - uint32_t col = average(src + offset, bsizex, yrest, xsize); - fill_block(dst + offset, bsizex, yrest, xsize, col); - offset += bsizex; - } - if (xrest > 0) - { - uint32_t col = average(src + offset, xrest, yrest, xsize); - fill_block(dst + offset, xrest, yrest, xsize, col); - } - } + offset = yi*bsizey*xsize; + for (xi = 0; xi < blocks_x; ++xi) + { + uint32_t col = average(src + offset, bsizex, bsizey, xsize); + fill_block(dst + offset, bsizex, bsizey, xsize, col); + offset += bsizex; + } + if (xrest > 0) + { + uint32_t col = average(src + offset, xrest, bsizey, xsize); + fill_block(dst + offset, xrest, bsizey, xsize, col); + } + } + // check for last line + if (yrest > 0) + { + offset = blocks_y*bsizey*xsize; + for (xi = 0; xi < blocks_x; ++xi) + { + uint32_t col = average(src + offset, bsizex, yrest, xsize); + fill_block(dst + offset, bsizex, yrest, xsize, col); + offset += bsizex; + } + if (xrest > 0) + { + uint32_t col = average(src + offset, xrest, yrest, xsize); + fill_block(dst + offset, xrest, yrest, xsize, col); + } + } + if (inst->pass_alpha) + { + for (xi = 0; xi < inst->width * inst->height; ++xi) + { + dst[xi] = (src[xi] & 0xff000000) | (dst[xi] & 0x00ffffff); + } + } } static uint32_t average(const uint32_t* start, - int bxsize, int bysize, int xsize) -{ - const uint32_t* p = start; - uint32_t alpha = 0, red = 0, green = 0, blue = 0; - uint32_t avg_alpha, avg_red, avg_green, avg_blue; - int x, y; - const uint32_t* pp; - uint32_t c; - - for (y = 0; y < bysize; ++y) - { - pp = p; - for (x = 0; x < bxsize; ++x) - { - c = *(pp++); - alpha += (c & 0xff000000) >> 24; - red += (c & 0x00ff0000) >> 16; - green += (c & 0x0000ff00) >> 8; - blue += (c & 0x000000ff); - } - p += xsize; - } - - avg_alpha = (alpha / (bxsize*bysize)) & 0xff; - avg_red = (red / (bxsize*bysize)) & 0xff; - avg_green = (green / (bxsize*bysize)) & 0xff; - avg_blue = (blue / (bxsize*bysize)) & 0xff; - - return (avg_alpha << 24) + (avg_red << 16) + (avg_green << 8) + avg_blue; + int bxsize, int bysize, int xsize) +{ + const uint32_t* p = start; + uint32_t alpha = 0, red = 0, green = 0, blue = 0; + uint32_t avg_alpha, avg_red, avg_green, avg_blue; + int x, y; + const uint32_t* pp; + uint32_t c; + + for (y = 0; y < bysize; ++y) + { + pp = p; + for (x = 0; x < bxsize; ++x) + { + c = *(pp++); + alpha += (c & 0xff000000) >> 24; + red += (c & 0x00ff0000) >> 16; + green += (c & 0x0000ff00) >> 8; + blue += (c & 0x000000ff); + } + p += xsize; + } + + avg_alpha = (alpha / (bxsize*bysize)) & 0xff; + avg_red = (red / (bxsize*bysize)) & 0xff; + avg_green = (green / (bxsize*bysize)) & 0xff; + avg_blue = (blue / (bxsize*bysize)) & 0xff; + + return (avg_alpha << 24) + (avg_red << 16) + (avg_green << 8) + avg_blue; } static void fill_block(uint32_t* start, int bxsize, int bysize, int xsize, uint32_t col) { - uint32_t* p = start; - int x, y; - uint32_t* pp; - - for (y = 0; y < bysize; ++y) - { - pp = p; - for (x = 0; x < bxsize; ++x) - { - *(pp++) = col; - } - p += xsize; - } - -} - + uint32_t* p = start; + int x, y; + uint32_t* pp; + + for (y = 0; y < bysize; ++y) + { + pp = p; + for (x = 0; x < bxsize; ++x) + { + *(pp++) = col; + } + p += xsize; + } + +} + diff --git a/src/filter/posterize/posterize.c b/src/filter/posterize/posterize.c index 12c65c1..931acd1 100644 --- a/src/filter/posterize/posterize.c +++ b/src/filter/posterize/posterize.c @@ -26,7 +26,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct posterize_instance { diff --git a/src/filter/premultiply/premultiply.cpp b/src/filter/premultiply/premultiply.cpp index 65a183e..dbe6486 100644 --- a/src/filter/premultiply/premultiply.cpp +++ b/src/filter/premultiply/premultiply.cpp @@ -17,7 +17,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" class Premultiply : public frei0r::filter { diff --git a/src/filter/rgbnoise/rgbnoise.c b/src/filter/rgbnoise/rgbnoise.c index dd40496..f51cd90 100644 --- a/src/filter/rgbnoise/rgbnoise.c +++ b/src/filter/rgbnoise/rgbnoise.c @@ -27,7 +27,7 @@ #include #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" static int MY_MAX_RAND = 32767;// I assume RAND_MAX to be at least this big. static double gaussian_lookup[32767]; diff --git a/src/filter/saturat0r/saturat0r.c b/src/filter/saturat0r/saturat0r.c index e1e7a1d..ee357a3 100644 --- a/src/filter/saturat0r/saturat0r.c +++ b/src/filter/saturat0r/saturat0r.c @@ -21,7 +21,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define MAX_SATURATION 8.0 diff --git a/src/filter/sigmoidaltransfer/sigmoidaltransfer.c b/src/filter/sigmoidaltransfer/sigmoidaltransfer.c index 52090ae..0ad141f 100644 --- a/src/filter/sigmoidaltransfer/sigmoidaltransfer.c +++ b/src/filter/sigmoidaltransfer/sigmoidaltransfer.c @@ -26,7 +26,7 @@ #include #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #define SIGMOIDAL_BASE 2 #define SIGMOIDAL_RANGE 20 diff --git a/src/filter/sobel/sobel.cpp b/src/filter/sobel/sobel.cpp index 95a1c72..f05860f 100644 --- a/src/filter/sobel/sobel.cpp +++ b/src/filter/sobel/sobel.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include class sobel : public frei0r::filter diff --git a/src/filter/softglow/softglow.c b/src/filter/softglow/softglow.c index 11000a6..a6c3cc8 100644 --- a/src/filter/softglow/softglow.c +++ b/src/filter/softglow/softglow.c @@ -27,8 +27,8 @@ #include #include #include "frei0r.h" -#include "blur.h" -#include "frei0r_math.h" +#include "frei0r/blur.h" +#include "frei0r/math.h" #define SIGMOIDAL_BASE 2 #define SIGMOIDAL_RANGE 20 diff --git a/src/filter/sopsat/sopsat.cpp b/src/filter/sopsat/sopsat.cpp index 952eba2..7d28603 100644 --- a/src/filter/sopsat/sopsat.cpp +++ b/src/filter/sopsat/sopsat.cpp @@ -20,7 +20,7 @@ #include #include #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" /** This filter implements a standard way of color correction proposed by diff --git a/src/filter/squareblur/squareblur.c b/src/filter/squareblur/squareblur.c index 64fed98..a703c02 100644 --- a/src/filter/squareblur/squareblur.c +++ b/src/filter/squareblur/squareblur.c @@ -24,7 +24,7 @@ #include #include "frei0r.h" -#include "blur.h" +#include "frei0r/blur.h" int f0r_init() { diff --git a/src/filter/three_point_balance/CMakeLists.txt b/src/filter/three_point_balance/CMakeLists.txt index b3e09f3..c2e6a94 100644 --- a/src/filter/three_point_balance/CMakeLists.txt +++ b/src/filter/three_point_balance/CMakeLists.txt @@ -1,6 +1,6 @@ # Set C99 flag for gcc if (CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "-std=c99") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") endif (CMAKE_COMPILER_IS_GNUCC) set (SOURCES three_point_balance.c) diff --git a/src/filter/three_point_balance/three_point_balance.c b/src/filter/three_point_balance/three_point_balance.c index dab3b61..7a3a0df 100644 --- a/src/filter/three_point_balance/three_point_balance.c +++ b/src/filter/three_point_balance/three_point_balance.c @@ -24,7 +24,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct three_point_balance_instance { diff --git a/src/filter/tint0r/tint0r.c b/src/filter/tint0r/tint0r.c index f3bbb15..2b96599 100644 --- a/src/filter/tint0r/tint0r.c +++ b/src/filter/tint0r/tint0r.c @@ -21,7 +21,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct tint0r_instance { diff --git a/src/filter/transparency/transparency.c b/src/filter/transparency/transparency.c index 1da05d5..7fa00c8 100644 --- a/src/filter/transparency/transparency.c +++ b/src/filter/transparency/transparency.c @@ -1,5 +1,5 @@ #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" #include #include diff --git a/src/filter/vignette/vignette.cpp b/src/filter/vignette/vignette.cpp index 3aa7281..2de1aa0 100644 --- a/src/filter/vignette/vignette.cpp +++ b/src/filter/vignette/vignette.cpp @@ -22,7 +22,7 @@ #endif /* _MSC_VER */ #include #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" /** diff --git a/src/mixer2/CMakeLists.txt b/src/mixer2/CMakeLists.txt index ee327af..7d93f65 100644 --- a/src/mixer2/CMakeLists.txt +++ b/src/mixer2/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory (difference) add_subdirectory (divide) add_subdirectory (dodge) +add_subdirectory (euclid_eraser) add_subdirectory (grain_extract) add_subdirectory (grain_merge) add_subdirectory (hardlight) diff --git a/src/mixer2/addition/addition.cpp b/src/mixer2/addition/addition.cpp index 650f8f5..cf5af78 100644 --- a/src/mixer2/addition/addition.cpp +++ b/src/mixer2/addition/addition.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/addition_alpha/addition_alpha.cpp b/src/mixer2/addition_alpha/addition_alpha.cpp index 92b5f2c..10f52f4 100644 --- a/src/mixer2/addition_alpha/addition_alpha.cpp +++ b/src/mixer2/addition_alpha/addition_alpha.cpp @@ -20,7 +20,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/alphaatop/alphaatop.cpp b/src/mixer2/alphaatop/alphaatop.cpp index 131c636..3e5b37f 100644 --- a/src/mixer2/alphaatop/alphaatop.cpp +++ b/src/mixer2/alphaatop/alphaatop.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include diff --git a/src/mixer2/alphain/alphain.cpp b/src/mixer2/alphain/alphain.cpp index 0725b11..d7e072c 100644 --- a/src/mixer2/alphain/alphain.cpp +++ b/src/mixer2/alphain/alphain.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include diff --git a/src/mixer2/alphaout/alphaout.cpp b/src/mixer2/alphaout/alphaout.cpp index 596616a..c6c585c 100644 --- a/src/mixer2/alphaout/alphaout.cpp +++ b/src/mixer2/alphaout/alphaout.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include diff --git a/src/mixer2/alphaover/alphaover.cpp b/src/mixer2/alphaover/alphaover.cpp index 8d36062..b83e7c2 100644 --- a/src/mixer2/alphaover/alphaover.cpp +++ b/src/mixer2/alphaover/alphaover.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include diff --git a/src/mixer2/alphaxor/alphaxor.cpp b/src/mixer2/alphaxor/alphaxor.cpp index 5cdcc62..3213560 100644 --- a/src/mixer2/alphaxor/alphaxor.cpp +++ b/src/mixer2/alphaxor/alphaxor.cpp @@ -18,7 +18,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #include diff --git a/src/mixer2/blend/blend.cpp b/src/mixer2/blend/blend.cpp index 9387af9..20ac884 100644 --- a/src/mixer2/blend/blend.cpp +++ b/src/mixer2/blend/blend.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/burn/burn.cpp b/src/mixer2/burn/burn.cpp index e5e6eb1..66622d4 100644 --- a/src/mixer2/burn/burn.cpp +++ b/src/mixer2/burn/burn.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/cairoaffineblend/cairoaffineblend.c b/src/mixer2/cairoaffineblend/cairoaffineblend.c index 2b0311c..a475b52 100644 --- a/src/mixer2/cairoaffineblend/cairoaffineblend.c +++ b/src/mixer2/cairoaffineblend/cairoaffineblend.c @@ -27,7 +27,7 @@ #include #include "frei0r.h" -#include "frei0r_cairo.h" +#include "frei0r/cairo.h" double PI=3.14159265358979; diff --git a/src/mixer2/cairoblend/cairoblend.c b/src/mixer2/cairoblend/cairoblend.c index 967c4cc..1fa1ac0 100644 --- a/src/mixer2/cairoblend/cairoblend.c +++ b/src/mixer2/cairoblend/cairoblend.c @@ -26,7 +26,7 @@ #include #include "frei0r.h" -#include "frei0r_cairo.h" +#include "frei0r/cairo.h" typedef struct cairo_blend_instance { diff --git a/src/mixer2/color_only/color_only.cpp b/src/mixer2/color_only/color_only.cpp index 10a9d8b..b83f21f 100644 --- a/src/mixer2/color_only/color_only.cpp +++ b/src/mixer2/color_only/color_only.cpp @@ -19,8 +19,8 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" -#include "frei0r_colorspace.h" +#include "frei0r/math.h" +#include "frei0r/colorspace.h" #define NBYTES 4 diff --git a/src/mixer2/composition/composition.c b/src/mixer2/composition/composition.c index 926d773..bac1894 100644 --- a/src/mixer2/composition/composition.c +++ b/src/mixer2/composition/composition.c @@ -21,7 +21,7 @@ #include #include "frei0r.h" -#include "frei0r_math.h" +#include "frei0r/math.h" typedef struct composition_instance { diff --git a/src/mixer2/darken/darken.cpp b/src/mixer2/darken/darken.cpp index 002aeab..b20df85 100644 --- a/src/mixer2/darken/darken.cpp +++ b/src/mixer2/darken/darken.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/difference/difference.cpp b/src/mixer2/difference/difference.cpp index 9a6cb97..c0f3487 100644 --- a/src/mixer2/difference/difference.cpp +++ b/src/mixer2/difference/difference.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/divide/divide.cpp b/src/mixer2/divide/divide.cpp index da9ec1c..4bff77b 100644 --- a/src/mixer2/divide/divide.cpp +++ b/src/mixer2/divide/divide.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/dodge/dodge.cpp b/src/mixer2/dodge/dodge.cpp index 47cd6fa..4190e67 100644 --- a/src/mixer2/dodge/dodge.cpp +++ b/src/mixer2/dodge/dodge.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/euclid_eraser/CMakeLists.txt b/src/mixer2/euclid_eraser/CMakeLists.txt new file mode 100644 index 0000000..4e020ad --- /dev/null +++ b/src/mixer2/euclid_eraser/CMakeLists.txt @@ -0,0 +1,11 @@ +set (SOURCES euclid_eraser.cpp) +set (TARGET euclid_eraser) + +if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_1_1_DEF}) +endif (MSVC) + +add_library (${TARGET} MODULE ${SOURCES}) +set_target_properties (${TARGET} PROPERTIES PREFIX "") + +install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) diff --git a/src/mixer2/euclid_eraser/euclid_eraser.cpp b/src/mixer2/euclid_eraser/euclid_eraser.cpp new file mode 100644 index 0000000..35c9073 --- /dev/null +++ b/src/mixer2/euclid_eraser/euclid_eraser.cpp @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) 2024 Erik H. Beck, bacon@tahomasoft.com +// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html + +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + * Also see: + * + * https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html + * + */ + +/** This is the source file for the euclid eraser two-input mixer for + frei0r. It uses euclidean distance to remove a static background + from a video. See file ./euclid_eraser.md for more info +*/ + +#include +#include "frei0r.hpp" +#include "frei0r/math.h" + +#define NBYTES 4 +#define CHANNELS 3 // Actually 4; 0-3 + +double euclidDistance(uint8_t x_r, uint8_t x_g, uint8_t x_b, + uint8_t y_r, uint8_t y_g, uint8_t y_b) + { + //calculating color channel differences for next steps + double red_d = x_r - y_r; + double green_d = x_g - y_g; + double blue_d = x_b - y_b; + + double sq_sum, dist; + + //calculating Euclidean distance + sq_sum = pow(red_d, 2) + pow(green_d, 2) + pow (blue_d, 2); + dist = sqrt(sq_sum); + + return dist; + } + + +class euclid_eraser : public frei0r::mixer2 +{ + +public: + euclid_eraser(unsigned int width, unsigned int height) + { + threshold = 5.6; // Default distance threshold value + register_param(threshold, "threshold", "Matching Threshold"); + } + + void update(double time, + uint32_t* out, + const uint32_t* in1, + const uint32_t* in2) + { + + const uint8_t *src1 = reinterpret_cast(in1); //frst track (0) + const uint8_t *src2 = reinterpret_cast(in2); //second trk (1) + uint8_t *dst = reinterpret_cast(out); + + double e_dist; + + uint32_t sizeCounter = size; + uint32_t b; + + while (sizeCounter--) + { + + // Loop over rgb + // Copy pixels from src2 to destination + + for (b=0; b<3; b++) + { + dst[b]=src2[b]; + } + e_dist=euclidDistance(src1[0],src1[1],src1[2], + src2[0],src2[1],src2[2]); + + if (e_dist <= euclid_eraser::threshold) { + // Make alpha channel for pixel fully transparent + dst[3]=0; + } + else { + // Make alpha channel for the pixel fully opaque + dst[3]=255; + } + + src1 += NBYTES; + src2 += NBYTES; + dst += NBYTES; + } + + } +private: + double threshold; + +}; + +frei0r::construct plugin("euclid_eraser", + "Erasing backgrounds with euclidian distance", + "Erik H. Beck", + 0,1, + F0R_COLOR_MODEL_RGBA8888); + diff --git a/src/mixer2/euclid_eraser/euclid_eraser.md b/src/mixer2/euclid_eraser/euclid_eraser.md new file mode 100644 index 0000000..313ba70 --- /dev/null +++ b/src/mixer2/euclid_eraser/euclid_eraser.md @@ -0,0 +1,88 @@ +# Euclid Eraser Notes # + +## Overview ## +This is about the Euclid Eraser mixer. + + +## Description ## + +This class is intended to operate as a mixer, taking two inputs and +yielding one output. + +The first input is a reference input, such as a single image or a +stretched video of a single image. + +The second input is the video stream to operate on. + +The output is a clone of the RGB data of the second input, but with +the alpha channel modified. + +This mixer takes the (first) reference input, such as a static +background, and removes it from every frame in the video stream of the +second input. + +The alpha channel on the output is based on the euclidian distance of +the two input coordinates in 3-d RGB space. If the calculated distance +betwen the two inputs for a given pixel is less than a provided +(variable) threshold amount, that indicates the pixel in the +background (reference) image is the same or similar enough to the +operational (second) input that is part of the background to be +removed, and the transparency is set to fully transparent via the +alpha channel (set to 0). + +If the calcuated distance exceeds the threshold, then that pixel is +part of the foreground image to be retained, and the transparency +of it is set to be fully opaque (alpha channel for that pixel set +to 255). + +## Basic Algorithm ## +The basic comparison algorithm is: + + +x is reference image +y is comparison image to remove reference image from + + +``` +float euclidDistance (int x_r, int x_g, int x_b, int y_r, int y_g, int y_b) +{ + //calculating color channel differences for next steps + float red_d = x_r - y_r; + float green_d = x_g - y_g; + float blue_d = x_b - y_b; + + float sq_sum, dist; + + //calculating Euclidean distance + sq_sum = pow(red_d, 2) + pow(green_d, 2) + pow (blue_d) + dist = sqrt(sq_sum); + + return dist; +} +``` + +## Note ## + +Here's a handy reminder on how the bits map up. + +``` + red_src1 = src1[0]; + green_src1 = src1[1]; + blue_src1 = src1[2]; + alpha_src1 = src1[3] + + red_src2 = src2[0]; + green_src2 = src2[1]; + blue_src2 = src2[2]; + alpha_src2 = src2[3] +``` +## Further Work ## + +Some potential improvements are: + +- Faster performance (math calculations, others) +- Options beyond binary for alpha + + + + diff --git a/src/mixer2/grain_extract/grain_extract.cpp b/src/mixer2/grain_extract/grain_extract.cpp index 5e5e519..3f047a2 100644 --- a/src/mixer2/grain_extract/grain_extract.cpp +++ b/src/mixer2/grain_extract/grain_extract.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/grain_merge/grain_merge.cpp b/src/mixer2/grain_merge/grain_merge.cpp index 6f88e95..9decc1c 100644 --- a/src/mixer2/grain_merge/grain_merge.cpp +++ b/src/mixer2/grain_merge/grain_merge.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/hardlight/hardlight.cpp b/src/mixer2/hardlight/hardlight.cpp index 07d55dd..9051be0 100644 --- a/src/mixer2/hardlight/hardlight.cpp +++ b/src/mixer2/hardlight/hardlight.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/hue/hue.cpp b/src/mixer2/hue/hue.cpp index 3304af4..4dc66b2 100644 --- a/src/mixer2/hue/hue.cpp +++ b/src/mixer2/hue/hue.cpp @@ -19,8 +19,8 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" -#include "frei0r_colorspace.h" +#include "frei0r/math.h" +#include "frei0r/colorspace.h" #define NBYTES 4 diff --git a/src/mixer2/lighten/lighten.cpp b/src/mixer2/lighten/lighten.cpp index 76922b9..56a36e4 100644 --- a/src/mixer2/lighten/lighten.cpp +++ b/src/mixer2/lighten/lighten.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/multiply/multiply.cpp b/src/mixer2/multiply/multiply.cpp index 41de211..30d9042 100644 --- a/src/mixer2/multiply/multiply.cpp +++ b/src/mixer2/multiply/multiply.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/overlay/overlay.cpp b/src/mixer2/overlay/overlay.cpp index 75386ca..dd48fb7 100644 --- a/src/mixer2/overlay/overlay.cpp +++ b/src/mixer2/overlay/overlay.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/saturation/saturation.cpp b/src/mixer2/saturation/saturation.cpp index 4e8aeb9..89a786e 100644 --- a/src/mixer2/saturation/saturation.cpp +++ b/src/mixer2/saturation/saturation.cpp @@ -19,8 +19,8 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" -#include "frei0r_colorspace.h" +#include "frei0r/math.h" +#include "frei0r/colorspace.h" #define NBYTES 4 diff --git a/src/mixer2/screen/screen.cpp b/src/mixer2/screen/screen.cpp index 7b82e0c..62813b4 100644 --- a/src/mixer2/screen/screen.cpp +++ b/src/mixer2/screen/screen.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/softlight/softlight.cpp b/src/mixer2/softlight/softlight.cpp index 98aef94..42936ac 100644 --- a/src/mixer2/softlight/softlight.cpp +++ b/src/mixer2/softlight/softlight.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/subtract/subtract.cpp b/src/mixer2/subtract/subtract.cpp index 93c509e..7ef49a7 100644 --- a/src/mixer2/subtract/subtract.cpp +++ b/src/mixer2/subtract/subtract.cpp @@ -19,7 +19,7 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" +#include "frei0r/math.h" #define NBYTES 4 #define ALPHA 3 diff --git a/src/mixer2/value/value.cpp b/src/mixer2/value/value.cpp index 96a48cb..4788386 100644 --- a/src/mixer2/value/value.cpp +++ b/src/mixer2/value/value.cpp @@ -19,8 +19,8 @@ */ #include "frei0r.hpp" -#include "frei0r_math.h" -#include "frei0r_colorspace.h" +#include "frei0r/math.h" +#include "frei0r/colorspace.h" #define NBYTES 4