New upstream version 2.4.0
IOhannes m zmölnig (Debian/GNU)
9 months ago
| 22 | 22 | release: ${{ steps.tag_release.outputs.release }} |
| 23 | 23 | version: ${{ steps.tag_release.outputs.version }} |
| 24 | 24 | steps: |
| 25 | - uses: actions/checkout@v3 | |
| 25 | - uses: actions/checkout@v4 | |
| 26 | 26 | - name: Setup Node.js |
| 27 | 27 | uses: actions/setup-node@v3 |
| 28 | 28 | with: |
| 50 | 50 | needs: [semantic-release] |
| 51 | 51 | if: ${{ needs.semantic-release.outputs.release == 'True' }} |
| 52 | 52 | steps: |
| 53 | - uses: actions/checkout@v3 | |
| 53 | - uses: actions/checkout@v4 | |
| 54 | 54 | - name: apt install deps |
| 55 | 55 | run: | |
| 56 | 56 | sudo apt-get update -y -q |
| 61 | 61 | cmake -G "Ninja" ../ |
| 62 | 62 | ninja |
| 63 | 63 | - name: Upload linux filter |
| 64 | uses: actions/upload-artifact@v3 | |
| 64 | uses: actions/upload-artifact@v4 | |
| 65 | 65 | with: |
| 66 | 66 | name: release-linux-filter |
| 67 | 67 | path: build/src/filter/**/*.so |
| 68 | 68 | - name: Upload linux mixer2 |
| 69 | uses: actions/upload-artifact@v3 | |
| 69 | uses: actions/upload-artifact@v4 | |
| 70 | 70 | with: |
| 71 | 71 | name: release-linux-mixer2 |
| 72 | 72 | path: build/src/mixer2/**/*.so |
| 73 | 73 | - name: Upload linux mixer3 |
| 74 | uses: actions/upload-artifact@v3 | |
| 74 | uses: actions/upload-artifact@v4 | |
| 75 | 75 | with: |
| 76 | 76 | name: release-linux-mixer3 |
| 77 | 77 | path: build/src/mixer3/**/*.so |
| 78 | 78 | - name: Upload linux generator |
| 79 | uses: actions/upload-artifact@v3 | |
| 79 | uses: actions/upload-artifact@v4 | |
| 80 | 80 | with: |
| 81 | 81 | name: release-linux-generator |
| 82 | 82 | path: build/src/generator/**/*.so |
| 87 | 87 | needs: [semantic-release] |
| 88 | 88 | if: ${{ needs.semantic-release.outputs.release == 'True' }} |
| 89 | 89 | steps: |
| 90 | - uses: actions/checkout@v3 | |
| 90 | - uses: actions/checkout@v4 | |
| 91 | 91 | - uses: ilammy/msvc-dev-cmd@v1 |
| 92 | 92 | - name: choco install deps |
| 93 | 93 | uses: crazy-max/ghaction-chocolatey@v2 |
| 99 | 99 | cmake -G "NMake Makefiles" ../ |
| 100 | 100 | nmake |
| 101 | 101 | - name: Upload win64 filter |
| 102 | uses: actions/upload-artifact@v3 | |
| 102 | uses: actions/upload-artifact@v4 | |
| 103 | 103 | with: |
| 104 | 104 | name: release-win64-filter |
| 105 | 105 | path: build/src/filter/**/*.dll |
| 106 | 106 | - name: Upload win64 mixer2 |
| 107 | uses: actions/upload-artifact@v3 | |
| 107 | uses: actions/upload-artifact@v4 | |
| 108 | 108 | with: |
| 109 | 109 | name: release-win64-mixer2 |
| 110 | 110 | path: build/src/mixer2/**/*.dll |
| 111 | 111 | - name: Upload win64 mixer3 |
| 112 | uses: actions/upload-artifact@v3 | |
| 112 | uses: actions/upload-artifact@v4 | |
| 113 | 113 | with: |
| 114 | 114 | name: release-win64-mixer3 |
| 115 | 115 | path: build/src/mixer3/**/*.dll |
| 116 | 116 | - name: Upload win64 generator |
| 117 | uses: actions/upload-artifact@v3 | |
| 117 | uses: actions/upload-artifact@v4 | |
| 118 | 118 | with: |
| 119 | 119 | name: release-win64-generator |
| 120 | 120 | path: build/src/generator/**/*.dll |
| 125 | 125 | needs: [semantic-release] |
| 126 | 126 | if: ${{ needs.semantic-release.outputs.release == 'True' }} |
| 127 | 127 | steps: |
| 128 | - uses: actions/checkout@v3 | |
| 128 | - uses: actions/checkout@v4 | |
| 129 | 129 | - name: Update Homebrew |
| 130 | 130 | run: | |
| 131 | brew update --preinstall | |
| 131 | brew update | |
| 132 | 132 | - name: Install Homebrew dependencies |
| 133 | 133 | run: | |
| 134 | 134 | env HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1 \ |
| 135 | brew install cmake ninja cairo | |
| 135 | brew install ninja cairo | |
| 136 | 136 | - name: Build using ninja |
| 137 | 137 | run: | |
| 138 | 138 | mkdir build && cd build |
| 139 | 139 | cmake -G "Ninja" ../ |
| 140 | 140 | ninja |
| 141 | 141 | - name: Upload osx filter |
| 142 | uses: actions/upload-artifact@v3 | |
| 142 | uses: actions/upload-artifact@v4 | |
| 143 | 143 | with: |
| 144 | 144 | name: release-osx-filter |
| 145 | 145 | path: build/src/filter/**/*.so |
| 146 | 146 | - name: Upload osx mixer2 |
| 147 | uses: actions/upload-artifact@v3 | |
| 147 | uses: actions/upload-artifact@v4 | |
| 148 | 148 | with: |
| 149 | 149 | name: release-osx-mixer2 |
| 150 | 150 | path: build/src/mixer2/**/*.so |
| 151 | 151 | - name: Upload osx mixer3 |
| 152 | uses: actions/upload-artifact@v3 | |
| 152 | uses: actions/upload-artifact@v4 | |
| 153 | 153 | with: |
| 154 | 154 | name: release-osx-mixer3 |
| 155 | 155 | path: build/src/mixer3/**/*.so |
| 156 | 156 | - name: Upload osx generator |
| 157 | uses: actions/upload-artifact@v3 | |
| 157 | uses: actions/upload-artifact@v4 | |
| 158 | 158 | with: |
| 159 | 159 | name: release-osx-generator |
| 160 | 160 | path: build/src/generator/**/*.so |
| 165 | 165 | if: ${{ needs.semantic-release.outputs.release == 'True' }} |
| 166 | 166 | runs-on: ubuntu-latest |
| 167 | 167 | steps: |
| 168 | - uses: actions/checkout@v3 | |
| 168 | - uses: actions/checkout@v4 | |
| 169 | 169 | - name: download binary artifacts |
| 170 | uses: actions/download-artifact@v3 | |
| 170 | uses: actions/download-artifact@v4 | |
| 171 | 171 | with: |
| 172 | 172 | path: | |
| 173 | 173 | frei0r-bin |
| 182 | 182 | cp README.md $dst/README.txt |
| 183 | 183 | cp COPYING $dst/LICENSE.txt |
| 184 | 184 | cp ChangeLog $dst/ChangeLog.txt |
| 185 | cp AUTHORS $dst/AUTHORS.txt | |
| 185 | cp AUTHORS.md $dst/AUTHORS.txt | |
| 186 | 186 | cp include/frei0r.h include/frei0r.hpp $dst/ |
| 187 | 187 | echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt |
| 188 | 188 | zip -r -9 $dst.zip $dst |
| 196 | 196 | cp README.md $dst/README.txt |
| 197 | 197 | cp COPYING $dst/LICENSE.txt |
| 198 | 198 | cp ChangeLog $dst/ChangeLog.txt |
| 199 | cp AUTHORS $dst/AUTHORS.txt | |
| 199 | cp AUTHORS.md $dst/AUTHORS.txt | |
| 200 | 200 | cp include/frei0r.h include/frei0r.hpp $dst/ |
| 201 | 201 | echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt |
| 202 | 202 | zip -r -9 $dst.zip $dst |
| 210 | 210 | cp README.md $dst/README.txt |
| 211 | 211 | cp COPYING $dst/LICENSE.txt |
| 212 | 212 | cp ChangeLog $dst/ChangeLog.txt |
| 213 | cp AUTHORS $dst/AUTHORS.txt | |
| 213 | cp AUTHORS.md $dst/AUTHORS.txt | |
| 214 | 214 | cp include/frei0r.h include/frei0r.hpp $dst/ |
| 215 | 215 | echo "${{ needs.semantic-release.outputs.version }}" > $dst/VERSION.txt |
| 216 | 216 | tar cvfz $dst.tar.gz $dst |
| 26 | 26 | # name: 🚨 REUSE Compliance |
| 27 | 27 | # runs-on: ubuntu-latest |
| 28 | 28 | # steps: |
| 29 | # - uses: actions/checkout@v3 | |
| 29 | # - uses: actions/checkout@v4 | |
| 30 | 30 | # - uses: fsfe/reuse-action@v1 |
| 31 | 31 | |
| 32 | 32 | c-lint: |
| 34 | 34 | runs-on: ubuntu-latest |
| 35 | 35 | if: "!contains(github.event.pull_request.labels.*.name, 'skip-lint')" |
| 36 | 36 | steps: |
| 37 | - uses: actions/checkout@v3 | |
| 37 | - uses: actions/checkout@v4 | |
| 38 | 38 | - uses: reviewdog/action-cpplint@master |
| 39 | 39 | env: |
| 40 | 40 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 61 | 61 | ,-whitespace/semicolon\ |
| 62 | 62 | ,-build/include_subdir\ |
| 63 | 63 | ,-build/include_order\ |
| 64 | ,-build/header_guard\ | |
| 64 | 65 | " |
| 65 | 66 | |
| 66 | 67 | test-suite: |
| 73 | 74 | fail-fast: false |
| 74 | 75 | runs-on: ubuntu-latest |
| 75 | 76 | steps: |
| 76 | - uses: actions/checkout@v3 | |
| 77 | - uses: actions/checkout@v4 | |
| 77 | 78 | - name: install dependencies |
| 78 | 79 | run: | |
| 79 | 80 | sudo apt-get update -qy |
| 89 | 90 | run: | |
| 90 | 91 | cd test && make |
| 91 | 92 | - name: ${{ matrix.compiler }} upload plugin analysis |
| 92 | uses: actions/upload-artifact@v3 | |
| 93 | uses: actions/upload-artifact@v4 | |
| 93 | 94 | with: |
| 94 | 95 | name: release-plugin-analysis |
| 95 | 96 | path: test/*.json |
| 96 | ||
| 0 | # Frei0r developers union | |
| 1 | ||
| 2 | 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). | |
| 3 | ||
| 4 | Frei0r is maintained by [Jaromil](https://jaromil.dyne.org) for the Dyne.org Foundation. | |
| 5 | ||
| 6 | ## Developers who contributed, in alphabetic order: | |
| 7 | ||
| 8 | Akito Iwakura | |
| 9 | Albert Frisch | |
| 10 | Brendan Hack | |
| 11 | Brian Matherly | |
| 12 | Burkhard Plaum | |
| 13 | Carlo E. Prelz | |
| 14 | Christoph Willing | |
| 15 | Dan Dennedy | |
| 16 | Filippo Giunchedi | |
| 17 | Gabriel Finch (Salsaman) | |
| 18 | Georg Seidel | |
| 19 | Henner Zeller | |
| 20 | Hedde Bosman | |
| 21 | IOhannes M. Zmölnig | |
| 22 | Janne Liljeblad | |
| 23 | Jean-Baptiste Mardelle | |
| 24 | Jean-François Fortin Tam | |
| 25 | Jean-Sebastien Senecal | |
| 26 | Jerome Blanchi (d.j.a.y) | |
| 27 | Joshua M. Doe | |
| 28 | Luca Bigliardi | |
| 29 | Maksim Golovkin (Максим Головкин) | |
| 30 | Marko Cebokli | |
| 31 | Martin Bayer | |
| 32 | Mathieu Guindon | |
| 33 | Matthias Schnoell | |
| 34 | Nicolas Carion | |
| 35 | Niels Elburg | |
| 36 | Phillip Promesberger | |
| 37 | Raphael Graf | |
| 38 | Richard Spindler | |
| 39 | Richard Ling (Chungzuwalla) | |
| 40 | Robert Schweikert | |
| 41 | Ross Lagerwall | |
| 42 | Samuel Mimram | |
| 43 | Simon A. Eugster | |
| 44 | Sofian Audry | |
| 45 | Stefano Sabatini | |
| 46 | Steinar H. Gunderson | |
| 47 | Thomas Coldrick | |
| 48 | Thomas Perl | |
| 49 | Till Theato | |
| 50 | Vincent Pinon |
| 0 | # Frei0r developers union | |
| 0 | 1 | |
| 1 | Project initiated at the Piksel Festival in 2004 | |
| 2 | hosted by BEK - Bergen Senter for Elektronisk Kunst | |
| 3 | Maintained at the Dyne.org Foundation | |
| 2 | 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). | |
| 4 | 3 | |
| 5 | Developers who contributed, in alphabetic order: | |
| 4 | Frei0r is a Dyne.org project maintained by Denis "Jaromil" Roio and Dan Dennedy. | |
| 6 | 5 | |
| 7 | Akito Iwakura | |
| 8 | Albert Frisch | |
| 9 | Brian Matherly | |
| 10 | Burkhard Plaum | |
| 11 | Carlo E. Prelz | |
| 12 | Christoph Willing | |
| 13 | Dan Dennedy | |
| 14 | Denis Roio (Jaromil) | |
| 15 | Filippo Giunchedi | |
| 16 | Gabriel Finch (Salsaman) | |
| 17 | Georg Seidel | |
| 18 | Henner Zeller | |
| 19 | Hedde Bosman | |
| 20 | IOhannes M. Zmölnig | |
| 21 | Janne Liljeblad | |
| 22 | Jean-Baptiste Mardelle | |
| 23 | Jean-François Fortin Tam | |
| 24 | Jean-Sebastien Senecal | |
| 25 | Jerome Blanchi (d.j.a.y) | |
| 26 | Joshua M. Doe | |
| 27 | Luca Bigliardi | |
| 28 | Maksim Golovkin (Максим Головкин) | |
| 29 | Marko Cebokli | |
| 30 | Martin Bayer | |
| 31 | Mathieu Guindon | |
| 32 | Matthias Schnoell | |
| 33 | Nicolas Carion | |
| 34 | Niels Elburg | |
| 35 | Phillip Promesberger | |
| 36 | Raphael Graf | |
| 37 | Richard Spindler | |
| 38 | Richard Ling (Chungzuwalla) | |
| 39 | Robert Schweikert | |
| 40 | Ross Lagerwall | |
| 41 | Samuel Mimram | |
| 42 | Simon A. Eugster | |
| 43 | Sofian Audry | |
| 44 | Stefano Sabatini | |
| 45 | Steinar H. Gunderson | |
| 46 | Thomas Coldrick | |
| 47 | Thomas Perl | |
| 48 | Till Theato | |
| 49 | Vincent Pinon | |
| 6 | ## Developers who contributed, in alphabetic order: | |
| 7 | ||
| 8 | 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. |
| 0 | cmake_minimum_required (VERSION 3.1) | |
| 0 | cmake_minimum_required (VERSION 3.12...3.31) | |
| 1 | 1 | |
| 2 | 2 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) |
| 3 | 3 | |
| 34 | 34 | # --- custom targets: --- |
| 35 | 35 | INCLUDE( cmake/modules/TargetDistclean.cmake OPTIONAL) |
| 36 | 36 | |
| 37 | # 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 | |
| 38 | install (DIRECTORY include DESTINATION . FILES_MATCHING PATTERN "frei0r.h" PATTERN "msvc" EXCLUDE) | |
| 37 | install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) | |
| 39 | 38 | |
| 40 | 39 | # For code documentation run: doxygen doc/Doxyfile |
| 41 | 40 | # add_subdirectory (doc) |
| 2 | 2 | # Jan Woetzel 04/2003 |
| 3 | 3 | # |
| 4 | 4 | |
| 5 | IF (UNIX) | |
| 6 | ADD_CUSTOM_TARGET (distclean @echo cleaning for source distribution) | |
| 7 | SET(DISTCLEANED | |
| 5 | if(UNIX) | |
| 6 | add_custom_target(distclean | |
| 7 | COMMENT "cleaning for source distribution" | |
| 8 | ) | |
| 9 | set(DISTCLEANED | |
| 8 | 10 | cmake.depends |
| 9 | 11 | cmake.check_depends |
| 10 | 12 | CMakeCache.txt |
| 15 | 17 | gmon.out |
| 16 | 18 | *~ |
| 17 | 19 | ) |
| 18 | ||
| 19 | ADD_CUSTOM_COMMAND( | |
| 20 | DEPENDS clean | |
| 20 | add_custom_command( | |
| 21 | POST_BUILD | |
| 22 | COMMENT "running target clean" | |
| 23 | TARGET distclean | |
| 24 | COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean | |
| 25 | ) | |
| 26 | add_custom_command( | |
| 27 | POST_BUILD | |
| 21 | 28 | COMMENT "distribution clean" |
| 22 | 29 | COMMAND rm |
| 23 | 30 | ARGS -Rf CMakeTmp ${DISTCLEANED} |
| 24 | 31 | TARGET distclean |
| 25 | 32 | ) |
| 26 | ENDIF(UNIX) | |
| 33 | endif(UNIX) | |
| 27 | 34 | |
| 0 | /* blur.h | |
| 1 | * Copyright (C) 2004--2005 Mathieu Guindon | |
| 2 | * Julien Keable | |
| 3 | * Jean-Sebastien Senecal (js@drone.ws) | |
| 4 | * | |
| 5 | * Modified by Richard Spindler (richard.spindler AT gmail.com) for blurring in | |
| 6 | * the mask0mate Frei0r plugin. | |
| 7 | * | |
| 8 | * This program is free software; you can redistribute it and/or modify | |
| 9 | * it under the terms of the GNU General Public License as published by | |
| 10 | * the Free Software Foundation; either version 2 of the License, or | |
| 11 | * (at your option) any later version. | |
| 12 | * | |
| 13 | * This program is distributed in the hope that it will be useful, | |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | * GNU General Public License for more details. | |
| 17 | * | |
| 18 | * You should have received a copy of the GNU General Public License | |
| 19 | * along with this program; if not, write to the Free Software | |
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 21 | */ | |
| 22 | ||
| 23 | #include <stdlib.h> | |
| 24 | #include <assert.h> | |
| 25 | #include <string.h> | |
| 26 | ||
| 27 | #include "frei0r.h" | |
| 28 | ||
| 29 | #define SIZE_RGBA 4 | |
| 30 | ||
| 31 | static inline int MAX(int a, int b) | |
| 32 | { | |
| 33 | return (a > b ? a : b); | |
| 34 | } | |
| 35 | ||
| 36 | static inline int MIN(int a, int b) | |
| 37 | { | |
| 38 | return (a < b ? a : b); | |
| 39 | } | |
| 40 | ||
| 41 | static inline void subtract_acc(uint32_t *dst, const uint32_t *src) | |
| 42 | { | |
| 43 | int n=SIZE_RGBA; | |
| 44 | while (n--) | |
| 45 | *dst++ -= *src++; | |
| 46 | } | |
| 47 | ||
| 48 | static inline void add_acc(uint32_t *dst, const uint32_t *src) | |
| 49 | { | |
| 50 | int n=SIZE_RGBA; | |
| 51 | while (n--) | |
| 52 | *dst++ += *src++; | |
| 53 | } | |
| 54 | ||
| 55 | static inline void divide(unsigned char *dst, const uint32_t *src, const unsigned int val) | |
| 56 | { | |
| 57 | int n=SIZE_RGBA; | |
| 58 | while (n--) | |
| 59 | *dst++ = *src++ / val; | |
| 60 | } | |
| 61 | ||
| 62 | typedef struct squareblur_instance | |
| 63 | { | |
| 64 | unsigned int width; | |
| 65 | unsigned int height; | |
| 66 | double kernel; /* the kernel size, as a percentage of the biggest of width and height */ | |
| 67 | uint32_t *mem; /* memory accumulation matrix of uint32_t (size = acc_width*acc_height*SIZE_RGBA) */ | |
| 68 | uint32_t **acc; /* accumulation matrix of pointers to SIZE_RGBA consecutive uint32_t in mem (size = acc_width*acc_height) */ | |
| 69 | } squareblur_instance_t; | |
| 70 | ||
| 71 | /* Updates the summed area table. */ | |
| 72 | static void update_summed_area_table(squareblur_instance_t *inst, const uint32_t *src) | |
| 73 | { | |
| 74 | register unsigned char *iter_data; | |
| 75 | register uint32_t *iter_mem; | |
| 76 | register unsigned int i, x, y; | |
| 77 | ||
| 78 | uint32_t acc_buffer[SIZE_RGBA]; /* accumulation buffer */ | |
| 79 | ||
| 80 | unsigned int row_width; | |
| 81 | unsigned int width, height; | |
| 82 | unsigned int cell_size; | |
| 83 | ||
| 84 | /* Compute basic params. */ | |
| 85 | width = inst->width+1; | |
| 86 | height = inst->height+1; | |
| 87 | row_width = SIZE_RGBA * width; | |
| 88 | cell_size = SIZE_RGBA * sizeof(uint32_t); | |
| 89 | ||
| 90 | /* Init iterators. */ | |
| 91 | iter_data = (unsigned char*) src; | |
| 92 | iter_mem = inst->mem; | |
| 93 | ||
| 94 | /* Process first row (all zeros). */ | |
| 95 | memset(iter_mem, 0, row_width * cell_size); | |
| 96 | iter_mem += row_width; | |
| 97 | ||
| 98 | if (height >= 1) | |
| 99 | { | |
| 100 | /* Process second row. */ | |
| 101 | memset(acc_buffer, 0, cell_size); | |
| 102 | memset(iter_mem, 0, cell_size); /* first column is void */ | |
| 103 | iter_mem += SIZE_RGBA; | |
| 104 | for (x=1; x<width; ++x) | |
| 105 | { | |
| 106 | for (i=0; i<SIZE_RGBA; ++i) | |
| 107 | *iter_mem++ = (acc_buffer[i] += *iter_data++); | |
| 108 | } | |
| 109 | ||
| 110 | /* Process other rows. */ | |
| 111 | for (y=2; y<height; ++y) | |
| 112 | { | |
| 113 | /* Copy upper line. */ | |
| 114 | memcpy(iter_mem, iter_mem - row_width, row_width * sizeof(uint32_t)); | |
| 115 | ||
| 116 | /* Process row. */ | |
| 117 | memset(acc_buffer, 0, cell_size); | |
| 118 | memset(iter_mem, 0, cell_size); /* first column is void */ | |
| 119 | iter_mem += SIZE_RGBA; | |
| 120 | for (x=1; x<width; ++x) | |
| 121 | { | |
| 122 | for (i=0; i<SIZE_RGBA; ++i) | |
| 123 | *iter_mem++ += (acc_buffer[i] += *iter_data++); | |
| 124 | } | |
| 125 | } | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | static void blur_get_param_info(f0r_param_info_t* info, int param_index) | |
| 130 | { | |
| 131 | switch(param_index) | |
| 132 | { | |
| 133 | case 0: | |
| 134 | info->name = "Kernel size"; | |
| 135 | info->type = F0R_PARAM_DOUBLE; | |
| 136 | info->explanation = "The size of the kernel, as a proportion to its coverage of the image"; | |
| 137 | break; | |
| 138 | } | |
| 139 | } | |
| 140 | ||
| 141 | static f0r_instance_t blur_construct(unsigned int width, unsigned int height) | |
| 142 | { | |
| 143 | squareblur_instance_t* inst = | |
| 144 | (squareblur_instance_t*)malloc(sizeof(squareblur_instance_t)); | |
| 145 | unsigned int i; | |
| 146 | unsigned int acc_width, acc_height = height+1; | |
| 147 | uint32_t* iter_mem; | |
| 148 | uint32_t** iter_acc; | |
| 149 | /* set params */ | |
| 150 | inst->width = width; inst->height = height; | |
| 151 | acc_width = width+1; acc_height = height+1; | |
| 152 | inst->kernel = 0.0; | |
| 153 | /* allocate memory for the summed-area-table */ | |
| 154 | inst->mem = (uint32_t*) malloc(acc_width*acc_height*SIZE_RGBA*sizeof(uint32_t)); | |
| 155 | inst->acc = (uint32_t**) malloc(acc_width*acc_height*sizeof(uint32_t*)); | |
| 156 | /* point at the right place */ | |
| 157 | iter_mem = inst->mem; | |
| 158 | iter_acc = inst->acc; | |
| 159 | for (i=0; i<acc_width*acc_height; ++i) | |
| 160 | { | |
| 161 | *iter_acc++ = iter_mem; | |
| 162 | iter_mem += SIZE_RGBA; | |
| 163 | } | |
| 164 | return (f0r_instance_t)inst; | |
| 165 | } | |
| 166 | ||
| 167 | static void blur_destruct(f0r_instance_t instance) | |
| 168 | { | |
| 169 | squareblur_instance_t* inst = | |
| 170 | (squareblur_instance_t*)instance; | |
| 171 | free(inst->acc); | |
| 172 | free(inst->mem); | |
| 173 | free(instance); | |
| 174 | } | |
| 175 | ||
| 176 | static void blur_set_param_value(f0r_instance_t instance, | |
| 177 | f0r_param_t param, int param_index) | |
| 178 | { | |
| 179 | assert(instance); | |
| 180 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 181 | ||
| 182 | switch(param_index) | |
| 183 | { | |
| 184 | case 0: | |
| 185 | /* kernel size */ | |
| 186 | inst->kernel = *((double*)param); | |
| 187 | break; | |
| 188 | } | |
| 189 | } | |
| 190 | ||
| 191 | static void blur_get_param_value(f0r_instance_t instance, | |
| 192 | f0r_param_t param, int param_index) | |
| 193 | { | |
| 194 | assert(instance); | |
| 195 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 196 | ||
| 197 | switch(param_index) | |
| 198 | { | |
| 199 | case 0: | |
| 200 | *((double*)param) = inst->kernel; | |
| 201 | break; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | static void blur_update(f0r_instance_t instance, double time, | |
| 206 | const uint32_t* inframe, uint32_t* outframe) | |
| 207 | { | |
| 208 | assert(instance); | |
| 209 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 210 | ||
| 211 | unsigned int width = inst->width; | |
| 212 | unsigned int height = inst->height; | |
| 213 | unsigned int acc_width = width+1; /* width of the summed area table */ | |
| 214 | unsigned int max = MAX(width, height); | |
| 215 | unsigned int kernel_size = (unsigned int) (inst->kernel * max / 2.0); | |
| 216 | ||
| 217 | unsigned int x, y; | |
| 218 | unsigned int x0, x1, y0, y1; | |
| 219 | unsigned int area; | |
| 220 | ||
| 221 | if (kernel_size <= 0) | |
| 222 | { | |
| 223 | /* No blur, just copy image. */ | |
| 224 | memcpy(outframe, inframe, width*height*sizeof(uint32_t)); | |
| 225 | } | |
| 226 | else | |
| 227 | { | |
| 228 | assert(inst->acc); | |
| 229 | unsigned char* dst = (unsigned char*)outframe; | |
| 230 | uint32_t** acc = inst->acc; | |
| 231 | uint32_t sum[SIZE_RGBA]; | |
| 232 | unsigned int y0_offset, y1_offset; | |
| 233 | ||
| 234 | /* Compute the summed area table. */ | |
| 235 | update_summed_area_table(inst, inframe); | |
| 236 | ||
| 237 | /* Loop through the image's pixels. */ | |
| 238 | for (y=0;y<height;y++) | |
| 239 | { | |
| 240 | for (x=0;x<width;x++) | |
| 241 | { | |
| 242 | /* The kernel's coordinates. */ | |
| 243 | x0 = MAX(x - kernel_size, 0); | |
| 244 | x1 = MIN(x + kernel_size + 1, width); | |
| 245 | y0 = MAX(y - kernel_size, 0); | |
| 246 | y1 = MIN(y + kernel_size + 1, height); | |
| 247 | ||
| 248 | /* Get the sum in the current kernel. */ | |
| 249 | area = (x1-x0)*(y1-y0); | |
| 250 | ||
| 251 | y0_offset = y0*acc_width; | |
| 252 | y1_offset = y1*acc_width; | |
| 253 | ||
| 254 | /* it is assumed that (x0,y0) <= (x1,y1) */ | |
| 255 | memcpy(sum, acc[y1_offset + x1], SIZE_RGBA*sizeof(uint32_t)); | |
| 256 | subtract_acc(sum, acc[y1_offset + x0]); | |
| 257 | subtract_acc(sum, acc[y0_offset + x1]); | |
| 258 | add_acc(sum, acc[y0_offset + x0]); | |
| 259 | ||
| 260 | /* Take the mean and copy it to output. */ | |
| 261 | divide(dst, sum, area); | |
| 262 | ||
| 263 | /* Increment iterator. */ | |
| 264 | dst += SIZE_RGBA; | |
| 265 | } | |
| 266 | } | |
| 267 | } | |
| 268 | } | |
| 269 |
| 0 | /* blur.h | |
| 1 | * Copyright (C) 2004--2005 Mathieu Guindon | |
| 2 | * Julien Keable | |
| 3 | * Jean-Sebastien Senecal (js@drone.ws) | |
| 4 | * | |
| 5 | * Modified by Richard Spindler (richard.spindler AT gmail.com) for blurring in | |
| 6 | * the mask0mate Frei0r plugin. | |
| 7 | * | |
| 8 | * This program is free software; you can redistribute it and/or modify | |
| 9 | * it under the terms of the GNU General Public License as published by | |
| 10 | * the Free Software Foundation; either version 2 of the License, or | |
| 11 | * (at your option) any later version. | |
| 12 | * | |
| 13 | * This program is distributed in the hope that it will be useful, | |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 | * GNU General Public License for more details. | |
| 17 | * | |
| 18 | * You should have received a copy of the GNU General Public License | |
| 19 | * along with this program; if not, write to the Free Software | |
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 21 | */ | |
| 22 | ||
| 23 | #include <stdlib.h> | |
| 24 | #include <assert.h> | |
| 25 | #include <string.h> | |
| 26 | ||
| 27 | #include "frei0r.h" | |
| 28 | ||
| 29 | #define SIZE_RGBA 4 | |
| 30 | ||
| 31 | static inline int MAX(int a, int b) | |
| 32 | { | |
| 33 | return (a > b ? a : b); | |
| 34 | } | |
| 35 | ||
| 36 | static inline int MIN(int a, int b) | |
| 37 | { | |
| 38 | return (a < b ? a : b); | |
| 39 | } | |
| 40 | ||
| 41 | static inline void subtract_acc(uint32_t *dst, const uint32_t *src) | |
| 42 | { | |
| 43 | int n=SIZE_RGBA; | |
| 44 | while (n--) | |
| 45 | *dst++ -= *src++; | |
| 46 | } | |
| 47 | ||
| 48 | static inline void add_acc(uint32_t *dst, const uint32_t *src) | |
| 49 | { | |
| 50 | int n=SIZE_RGBA; | |
| 51 | while (n--) | |
| 52 | *dst++ += *src++; | |
| 53 | } | |
| 54 | ||
| 55 | static inline void divide(unsigned char *dst, const uint32_t *src, const unsigned int val) | |
| 56 | { | |
| 57 | int n=SIZE_RGBA; | |
| 58 | while (n--) | |
| 59 | *dst++ = *src++ / val; | |
| 60 | } | |
| 61 | ||
| 62 | typedef struct squareblur_instance | |
| 63 | { | |
| 64 | unsigned int width; | |
| 65 | unsigned int height; | |
| 66 | double kernel; /* the kernel size, as a percentage of the biggest of width and height */ | |
| 67 | uint32_t *mem; /* memory accumulation matrix of uint32_t (size = acc_width*acc_height*SIZE_RGBA) */ | |
| 68 | uint32_t **acc; /* accumulation matrix of pointers to SIZE_RGBA consecutive uint32_t in mem (size = acc_width*acc_height) */ | |
| 69 | } squareblur_instance_t; | |
| 70 | ||
| 71 | /* Updates the summed area table. */ | |
| 72 | static void update_summed_area_table(squareblur_instance_t *inst, const uint32_t *src) | |
| 73 | { | |
| 74 | register unsigned char *iter_data; | |
| 75 | register uint32_t *iter_mem; | |
| 76 | register unsigned int i, x, y; | |
| 77 | ||
| 78 | uint32_t acc_buffer[SIZE_RGBA]; /* accumulation buffer */ | |
| 79 | ||
| 80 | unsigned int row_width; | |
| 81 | unsigned int width, height; | |
| 82 | unsigned int cell_size; | |
| 83 | ||
| 84 | /* Compute basic params. */ | |
| 85 | width = inst->width+1; | |
| 86 | height = inst->height+1; | |
| 87 | row_width = SIZE_RGBA * width; | |
| 88 | cell_size = SIZE_RGBA * sizeof(uint32_t); | |
| 89 | ||
| 90 | /* Init iterators. */ | |
| 91 | iter_data = (unsigned char*) src; | |
| 92 | iter_mem = inst->mem; | |
| 93 | ||
| 94 | /* Process first row (all zeros). */ | |
| 95 | memset(iter_mem, 0, row_width * cell_size); | |
| 96 | iter_mem += row_width; | |
| 97 | ||
| 98 | if (height >= 1) | |
| 99 | { | |
| 100 | /* Process second row. */ | |
| 101 | memset(acc_buffer, 0, cell_size); | |
| 102 | memset(iter_mem, 0, cell_size); /* first column is void */ | |
| 103 | iter_mem += SIZE_RGBA; | |
| 104 | for (x=1; x<width; ++x) | |
| 105 | { | |
| 106 | for (i=0; i<SIZE_RGBA; ++i) | |
| 107 | *iter_mem++ = (acc_buffer[i] += *iter_data++); | |
| 108 | } | |
| 109 | ||
| 110 | /* Process other rows. */ | |
| 111 | for (y=2; y<height; ++y) | |
| 112 | { | |
| 113 | /* Copy upper line. */ | |
| 114 | memcpy(iter_mem, iter_mem - row_width, row_width * sizeof(uint32_t)); | |
| 115 | ||
| 116 | /* Process row. */ | |
| 117 | memset(acc_buffer, 0, cell_size); | |
| 118 | memset(iter_mem, 0, cell_size); /* first column is void */ | |
| 119 | iter_mem += SIZE_RGBA; | |
| 120 | for (x=1; x<width; ++x) | |
| 121 | { | |
| 122 | for (i=0; i<SIZE_RGBA; ++i) | |
| 123 | *iter_mem++ += (acc_buffer[i] += *iter_data++); | |
| 124 | } | |
| 125 | } | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | static void blur_get_param_info(f0r_param_info_t* info, int param_index) | |
| 130 | { | |
| 131 | switch(param_index) | |
| 132 | { | |
| 133 | case 0: | |
| 134 | info->name = "Kernel size"; | |
| 135 | info->type = F0R_PARAM_DOUBLE; | |
| 136 | info->explanation = "The size of the kernel, as a proportion to its coverage of the image"; | |
| 137 | break; | |
| 138 | } | |
| 139 | } | |
| 140 | ||
| 141 | static f0r_instance_t blur_construct(unsigned int width, unsigned int height) | |
| 142 | { | |
| 143 | squareblur_instance_t* inst = | |
| 144 | (squareblur_instance_t*)malloc(sizeof(squareblur_instance_t)); | |
| 145 | unsigned int i; | |
| 146 | unsigned int acc_width, acc_height = height+1; | |
| 147 | uint32_t* iter_mem; | |
| 148 | uint32_t** iter_acc; | |
| 149 | /* set params */ | |
| 150 | inst->width = width; inst->height = height; | |
| 151 | acc_width = width+1; acc_height = height+1; | |
| 152 | inst->kernel = 0.0; | |
| 153 | /* allocate memory for the summed-area-table */ | |
| 154 | inst->mem = (uint32_t*) malloc(acc_width*acc_height*SIZE_RGBA*sizeof(uint32_t)); | |
| 155 | inst->acc = (uint32_t**) malloc(acc_width*acc_height*sizeof(uint32_t*)); | |
| 156 | /* point at the right place */ | |
| 157 | iter_mem = inst->mem; | |
| 158 | iter_acc = inst->acc; | |
| 159 | for (i=0; i<acc_width*acc_height; ++i) | |
| 160 | { | |
| 161 | *iter_acc++ = iter_mem; | |
| 162 | iter_mem += SIZE_RGBA; | |
| 163 | } | |
| 164 | return (f0r_instance_t)inst; | |
| 165 | } | |
| 166 | ||
| 167 | static void blur_destruct(f0r_instance_t instance) | |
| 168 | { | |
| 169 | squareblur_instance_t* inst = | |
| 170 | (squareblur_instance_t*)instance; | |
| 171 | free(inst->acc); | |
| 172 | free(inst->mem); | |
| 173 | free(instance); | |
| 174 | } | |
| 175 | ||
| 176 | static void blur_set_param_value(f0r_instance_t instance, | |
| 177 | f0r_param_t param, int param_index) | |
| 178 | { | |
| 179 | assert(instance); | |
| 180 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 181 | ||
| 182 | switch(param_index) | |
| 183 | { | |
| 184 | case 0: | |
| 185 | /* kernel size */ | |
| 186 | inst->kernel = *((double*)param); | |
| 187 | break; | |
| 188 | } | |
| 189 | } | |
| 190 | ||
| 191 | static void blur_get_param_value(f0r_instance_t instance, | |
| 192 | f0r_param_t param, int param_index) | |
| 193 | { | |
| 194 | assert(instance); | |
| 195 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 196 | ||
| 197 | switch(param_index) | |
| 198 | { | |
| 199 | case 0: | |
| 200 | *((double*)param) = inst->kernel; | |
| 201 | break; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | static void blur_update(f0r_instance_t instance, double time, | |
| 206 | const uint32_t* inframe, uint32_t* outframe) | |
| 207 | { | |
| 208 | assert(instance); | |
| 209 | squareblur_instance_t* inst = (squareblur_instance_t*)instance; | |
| 210 | ||
| 211 | unsigned int width = inst->width; | |
| 212 | unsigned int height = inst->height; | |
| 213 | unsigned int acc_width = width+1; /* width of the summed area table */ | |
| 214 | unsigned int max = MAX(width, height); | |
| 215 | unsigned int kernel_size = (unsigned int) (inst->kernel * max / 2.0); | |
| 216 | ||
| 217 | unsigned int x, y; | |
| 218 | unsigned int x0, x1, y0, y1; | |
| 219 | unsigned int area; | |
| 220 | ||
| 221 | if (kernel_size <= 0) | |
| 222 | { | |
| 223 | /* No blur, just copy image. */ | |
| 224 | memcpy(outframe, inframe, width*height*sizeof(uint32_t)); | |
| 225 | } | |
| 226 | else | |
| 227 | { | |
| 228 | assert(inst->acc); | |
| 229 | unsigned char* dst = (unsigned char*)outframe; | |
| 230 | uint32_t** acc = inst->acc; | |
| 231 | uint32_t sum[SIZE_RGBA]; | |
| 232 | unsigned int y0_offset, y1_offset; | |
| 233 | ||
| 234 | /* Compute the summed area table. */ | |
| 235 | update_summed_area_table(inst, inframe); | |
| 236 | ||
| 237 | /* Loop through the image's pixels. */ | |
| 238 | for (y=0;y<height;y++) | |
| 239 | { | |
| 240 | for (x=0;x<width;x++) | |
| 241 | { | |
| 242 | /* The kernel's coordinates. */ | |
| 243 | x0 = MAX(x - kernel_size, 0); | |
| 244 | x1 = MIN(x + kernel_size + 1, width); | |
| 245 | y0 = MAX(y - kernel_size, 0); | |
| 246 | y1 = MIN(y + kernel_size + 1, height); | |
| 247 | ||
| 248 | /* Get the sum in the current kernel. */ | |
| 249 | area = (x1-x0)*(y1-y0); | |
| 250 | ||
| 251 | y0_offset = y0*acc_width; | |
| 252 | y1_offset = y1*acc_width; | |
| 253 | ||
| 254 | /* it is assumed that (x0,y0) <= (x1,y1) */ | |
| 255 | memcpy(sum, acc[y1_offset + x1], SIZE_RGBA*sizeof(uint32_t)); | |
| 256 | subtract_acc(sum, acc[y1_offset + x0]); | |
| 257 | subtract_acc(sum, acc[y0_offset + x1]); | |
| 258 | add_acc(sum, acc[y0_offset + x0]); | |
| 259 | ||
| 260 | /* Take the mean and copy it to output. */ | |
| 261 | divide(dst, sum, area); | |
| 262 | ||
| 263 | /* Increment iterator. */ | |
| 264 | dst += SIZE_RGBA; | |
| 265 | } | |
| 266 | } | |
| 267 | } | |
| 268 | } | |
| 269 |
| 0 | /* | |
| 1 | * frei0r/cairo.h | |
| 2 | * Copyright 2012 Janne Liljeblad | |
| 3 | * | |
| 4 | * This file is part of Frei0r. | |
| 5 | * | |
| 6 | * This program is free software; you can redistribute it and/or modify | |
| 7 | * it under the terms of the GNU General Public License as published by | |
| 8 | * the Free Software Foundation; either version 2 of the License, or | |
| 9 | * (at your option) any later version. | |
| 10 | * | |
| 11 | * This program is distributed in the hope that it will be useful, | |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | * GNU General Public License for more details. | |
| 15 | * | |
| 16 | * You should have received a copy of the GNU General Public License | |
| 17 | * along with this program; if not, write to the Free Software | |
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 19 | */ | |
| 20 | ||
| 21 | ||
| 22 | #include <cairo.h> | |
| 23 | #include <string.h> | |
| 24 | #include "frei0r/math.h" | |
| 25 | ||
| 26 | /** | |
| 27 | * String identifiers for gradient types available using Cairo. | |
| 28 | */ | |
| 29 | #define GRADIENT_LINEAR "gradient_linear" | |
| 30 | #define GRADIENT_RADIAL "gradient_radial" | |
| 31 | ||
| 32 | /** | |
| 33 | * String identifiers for blend modes available using Cairo. | |
| 34 | */ | |
| 35 | #define NORMAL "normal" | |
| 36 | #define ADD "add" | |
| 37 | #define SATURATE "saturate" | |
| 38 | #define MULTIPLY "multiply" | |
| 39 | #define SCREEN "screen" | |
| 40 | #define OVERLAY "overlay" | |
| 41 | #define DARKEN "darken" | |
| 42 | #define LIGHTEN "lighten" | |
| 43 | #define COLORDODGE "colordodge" | |
| 44 | #define COLORBURN "colorburn" | |
| 45 | #define HARDLIGHT "hardlight" | |
| 46 | #define SOFTLIGHT "softlight" | |
| 47 | #define DIFFERENCE "difference" | |
| 48 | #define EXCLUSION "exclusion" | |
| 49 | #define HSLHUE "hslhue" | |
| 50 | #define HSLSATURATION "hslsaturation" | |
| 51 | #define HSLCOLOR "hslcolor" | |
| 52 | #define HSLLUMINOSITY "hslluminosity" | |
| 53 | ||
| 54 | ||
| 55 | /** | |
| 56 | * frei0r_cairo_set_operator | |
| 57 | * @cr: Cairo context | |
| 58 | * @op: String identifier for a blend mode | |
| 59 | * | |
| 60 | * Sets cairo context to use the defined blend mode for all paint operations. | |
| 61 | */ | |
| 62 | void frei0r_cairo_set_operator(cairo_t *cr, char *op) | |
| 63 | { | |
| 64 | if(strcmp(op, NORMAL) == 0) | |
| 65 | { | |
| 66 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 67 | } | |
| 68 | else if(strcmp(op, ADD) == 0) | |
| 69 | { | |
| 70 | cairo_set_operator (cr, CAIRO_OPERATOR_ADD); | |
| 71 | } | |
| 72 | else if(strcmp(op, SATURATE) == 0) | |
| 73 | { | |
| 74 | cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); | |
| 75 | } | |
| 76 | else if(strcmp(op, MULTIPLY) == 0) | |
| 77 | { | |
| 78 | cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY); | |
| 79 | } | |
| 80 | else if(strcmp(op, SCREEN) == 0) | |
| 81 | { | |
| 82 | cairo_set_operator (cr, CAIRO_OPERATOR_SCREEN); | |
| 83 | } | |
| 84 | else if(strcmp(op, OVERLAY) == 0) | |
| 85 | { | |
| 86 | cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY); | |
| 87 | } | |
| 88 | else if(strcmp(op, DARKEN) == 0) | |
| 89 | { | |
| 90 | cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN); | |
| 91 | } | |
| 92 | else if(strcmp(op, LIGHTEN) == 0) | |
| 93 | { | |
| 94 | cairo_set_operator (cr, CAIRO_OPERATOR_LIGHTEN); | |
| 95 | } | |
| 96 | else if(strcmp(op, COLORDODGE) == 0) | |
| 97 | { | |
| 98 | cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE); | |
| 99 | } | |
| 100 | else if(strcmp(op, COLORBURN) == 0) | |
| 101 | { | |
| 102 | cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_BURN); | |
| 103 | } | |
| 104 | else if(strcmp(op, HARDLIGHT) == 0) | |
| 105 | { | |
| 106 | cairo_set_operator (cr, CAIRO_OPERATOR_HARD_LIGHT); | |
| 107 | } | |
| 108 | else if(strcmp(op, SOFTLIGHT) == 0) | |
| 109 | { | |
| 110 | cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT); | |
| 111 | } | |
| 112 | else if(strcmp(op, DIFFERENCE) == 0) | |
| 113 | { | |
| 114 | cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE); | |
| 115 | } | |
| 116 | else if(strcmp(op, EXCLUSION) == 0) | |
| 117 | { | |
| 118 | cairo_set_operator (cr, CAIRO_OPERATOR_EXCLUSION); | |
| 119 | } | |
| 120 | else if(strcmp(op, HSLHUE) == 0) | |
| 121 | { | |
| 122 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_HUE); | |
| 123 | } | |
| 124 | else if(strcmp(op, HSLSATURATION) == 0) | |
| 125 | { | |
| 126 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION); | |
| 127 | } | |
| 128 | else if(strcmp(op, HSLCOLOR) == 0) | |
| 129 | { | |
| 130 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_COLOR); | |
| 131 | } | |
| 132 | else if(strcmp(op, HSLLUMINOSITY ) == 0) | |
| 133 | { | |
| 134 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY); | |
| 135 | } | |
| 136 | else | |
| 137 | { | |
| 138 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | ||
| 143 | /** | |
| 144 | * frei0r_cairo_set_rgba_LITTLE_ENDIAN | |
| 145 | * @cr: Cairo context | |
| 146 | * @red: red component, 0 - 1 | |
| 147 | * @green: green component, 0 - 1 | |
| 148 | * @blue: blue component, 0 - 1 | |
| 149 | * @alpha: opacity of color, 0 -1 | |
| 150 | * | |
| 151 | * Sets cairo context to use the defined color paint operations. | |
| 152 | * Switches red and blue channels to get correct color on little endian machines. | |
| 153 | * This method only works correctly on little endian machines. | |
| 154 | */ | |
| 155 | void frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue, double alpha) | |
| 156 | { | |
| 157 | cairo_set_source_rgba (cr, blue, green, red, alpha); | |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * frei0r_cairo_set_rgb_LITTLE_ENDIAN | |
| 162 | * @cr: Cairo context | |
| 163 | * @red: red component, 0 - 1 | |
| 164 | * @green: green component, 0 - 1 | |
| 165 | * @blue: blue component, 0 - 1 | |
| 166 | * | |
| 167 | * Sets cairo context to use the defined color paint operations. | |
| 168 | * Switches red and blue channels to get correct color on little endian machines. | |
| 169 | * This method only works correctly on little endian machines. | |
| 170 | */ | |
| 171 | void frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue) | |
| 172 | { | |
| 173 | cairo_set_source_rgb (cr, blue, green, red); | |
| 174 | } | |
| 175 | ||
| 176 | /** | |
| 177 | * freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN( | |
| 178 | * @pat: Cairo pattern | |
| 179 | * @offset: offset of color position in pattern space, 0 - 1 | |
| 180 | * @red: red component, 0 - 1 | |
| 181 | * @green: green component, 0 - 1 | |
| 182 | * @blue: blue component, 0 - 1 | |
| 183 | * @alpha: opacity of color, 0 -1 | |
| 184 | * | |
| 185 | * Sets color stop for cairo patterns. | |
| 186 | * Switches red and blue channels to get correct color on little endian machines. | |
| 187 | * This method only works correctly on little endian machines. | |
| 188 | */ | |
| 189 | void freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t *pat, double offset, | |
| 190 | double red, double green, double blue, double alpha) | |
| 191 | { | |
| 192 | cairo_pattern_add_color_stop_rgba (pat, offset, blue, green, red, alpha); | |
| 193 | } | |
| 194 | ||
| 195 | /** | |
| 196 | * frei0r_cairo_get_pixel_position | |
| 197 | * @norm_pos: position in range 0 - 1, either x or y | |
| 198 | * @dim: dimension, either witdh or height | |
| 199 | * | |
| 200 | * Converts double range [0 -> 1] to pixel range [-2*dim -> 3*dim]. Input 0.4 gives position 0. | |
| 201 | * | |
| 202 | * Returns: position in pixels | |
| 203 | */ | |
| 204 | double frei0r_cairo_get_pixel_position (double norm_pos, int dim) | |
| 205 | { | |
| 206 | double pos_o = -(dim * 2.0); | |
| 207 | return pos_o + norm_pos * dim * 5.0; | |
| 208 | } | |
| 209 | ||
| 210 | /** | |
| 211 | * frei0r_cairo_get_scale | |
| 212 | * @norm_scale: scale in range 0 - 1 | |
| 213 | * | |
| 214 | * Converts double range [0 -> 1] to scale range [0 -> 5]. Input 0.2 gives scale 1.0. | |
| 215 | * | |
| 216 | * Returns: scale | |
| 217 | */ | |
| 218 | double frei0r_cairo_get_scale (double norm_scale) | |
| 219 | { | |
| 220 | return norm_scale * 5.0; | |
| 221 | } | |
| 222 | ||
| 223 | /** | |
| 224 | * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. | |
| 225 | * | |
| 226 | * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 | |
| 227 | * \param pixels the size of the image buffer in number of pixels | |
| 228 | * \param alpha if >= 0, the alpha channel will be set to this value | |
| 229 | * \see frei0r_cairo_unpremultiply_rgba | |
| 230 | */ | |
| 231 | void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) | |
| 232 | { | |
| 233 | int i = pixels + 1; | |
| 234 | while ( --i ) { | |
| 235 | register unsigned char a = rgba[3]; | |
| 236 | if (a == 0) { | |
| 237 | *((uint32_t *)rgba) = 0; | |
| 238 | } else if (a < 0xff) { | |
| 239 | rgba[0] = ( rgba[0] * a ) >> 8; | |
| 240 | rgba[1] = ( rgba[1] * a ) >> 8; | |
| 241 | rgba[2] = ( rgba[2] * a ) >> 8; | |
| 242 | } | |
| 243 | if (alpha >= 0) rgba[3] = alpha; | |
| 244 | rgba += 4; | |
| 245 | } | |
| 246 | } | |
| 247 | ||
| 248 | /** | |
| 249 | * Convert Cairo ARGB pre-multiplied alpha to frei0r straight RGBA. | |
| 250 | * | |
| 251 | * \param rgba the image buffer with format CAIRO_FORMAT_ARGB32 | |
| 252 | * \param pixels the size of the image buffer in number of pixels | |
| 253 | * \see frei0r_cairo_premultiply_rgba | |
| 254 | */ | |
| 255 | void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) | |
| 256 | { | |
| 257 | int i = pixels + 1; | |
| 258 | while ( --i ) { | |
| 259 | register unsigned char a = rgba[3]; | |
| 260 | if (a > 0 && a < 0xff) { | |
| 261 | rgba[0] = MIN(( rgba[0] << 8 ) / a, 255); | |
| 262 | rgba[1] = MIN(( rgba[1] << 8 ) / a, 255); | |
| 263 | rgba[2] = MIN(( rgba[2] << 8 ) / a, 255); | |
| 264 | } | |
| 265 | rgba += 4; | |
| 266 | } | |
| 267 | } | |
| 268 | ||
| 269 | /** | |
| 270 | * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. | |
| 271 | * | |
| 272 | * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 | |
| 273 | * \param pixels the size of the image buffer in number of pixels | |
| 274 | * \param alpha if >= 0, the alpha channel will be set to this value | |
| 275 | * \see frei0r_cairo_premultiply_rgba | |
| 276 | * | |
| 277 | * This is the same as frei0r_cairo_premultiply_rgba but it writes the | |
| 278 | * output to a different buffer. | |
| 279 | */ | |
| 280 | void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, | |
| 281 | int pixels, int alpha) | |
| 282 | { | |
| 283 | int i = pixels + 1; | |
| 284 | while ( --i ) { | |
| 285 | register unsigned char a = in[3]; | |
| 286 | if (a == 0) { | |
| 287 | *((uint32_t *)out) = 0; | |
| 288 | } else if (a == 0xff) { | |
| 289 | memcpy(out, in, 4); | |
| 290 | } else { | |
| 291 | out[0] = ( in[0] * a ) >> 8; | |
| 292 | out[1] = ( in[1] * a ) >> 8; | |
| 293 | out[2] = ( in[2] * a ) >> 8; | |
| 294 | if (alpha < 0) | |
| 295 | out[3] = a; | |
| 296 | } | |
| 297 | if (alpha >= 0) | |
| 298 | out[3] = alpha; | |
| 299 | in += 4; | |
| 300 | out += 4; | |
| 301 | } | |
| 302 | } |
| 0 | /* cfc.h | |
| 1 | * uchar->float and float->uchar conversion for packed RGBA video | |
| 2 | * with flexible gamma correction | |
| 3 | * | |
| 4 | * Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu | |
| 5 | * This file is a part of the Frei0r package | |
| 6 | * | |
| 7 | * This program is free software; you can redistribute it and/or modify | |
| 8 | * it under the terms of the GNU General Public License as published by | |
| 9 | * the Free Software Foundation; either version 2 of the License, or | |
| 10 | * (at your option) any later version. | |
| 11 | * | |
| 12 | * This program is distributed in the hope that it will be useful, | |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 | * GNU General Public License for more details. | |
| 16 | * | |
| 17 | * You should have received a copy of the GNU General Public License | |
| 18 | * along with this program; if not, write to the Free Software | |
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 20 | */ | |
| 21 | ||
| 22 | // the float to uint8_t conversion is done | |
| 23 | // using Spitzak type tables (upper 16 bits | |
| 24 | // of a float value used as table index) | |
| 25 | // see http://mysite.verizon.net/spitzak/conversion/ | |
| 26 | ||
| 27 | #include <math.h> | |
| 28 | #include <inttypes.h> | |
| 29 | ||
| 30 | ||
| 31 | typedef struct | |
| 32 | { | |
| 33 | float r; | |
| 34 | float g; | |
| 35 | float b; | |
| 36 | float a; | |
| 37 | } float_rgba; | |
| 38 | ||
| 39 | //------------------------------------------------------ | |
| 40 | //the following gamma functions need not be speed optimized, | |
| 41 | //as they are only used for table generation | |
| 42 | ||
| 43 | //-------------------------------------------------------- | |
| 44 | //rec 709 gamma forward (linear to gamma) | |
| 45 | //a = input, should be in the 0.0 to 1.0 range | |
| 46 | static inline float gamma_709_f(float a) | |
| 47 | { | |
| 48 | return (a<=0.018) ? 4.5*a : 1.099*powf(a,0.45)-0.099; | |
| 49 | } | |
| 50 | ||
| 51 | //-------------------------------------------------------- | |
| 52 | //rec 709 gamma backward (gamma to linear) | |
| 53 | //a = input, should be in the 0.0 to 1.0 range | |
| 54 | static inline float gamma_709_b(float a) | |
| 55 | { | |
| 56 | return (a<=0.081) ? a/4.5 : powf((a+0.099)/1.099,1.0/0.45); | |
| 57 | } | |
| 58 | ||
| 59 | //---------------------------------------------------- | |
| 60 | //sRGB gamma forward (linear to gamma) | |
| 61 | //a = input, should be in the 0.0 to 1.0 range | |
| 62 | static inline float gamma_sRGB_f(float a) | |
| 63 | { | |
| 64 | return (a<=0.0031308) ? 12.92*a : 1.055*powf(a,1.0/2.4)-0.055; | |
| 65 | } | |
| 66 | ||
| 67 | //-------------------------------------------------------- | |
| 68 | //sRGB gamma backward (gamma to linear) | |
| 69 | //a = input, should be in the 0.0 to 1.0 range | |
| 70 | static inline float gamma_sRGB_b(float a) | |
| 71 | { | |
| 72 | return (a<=0.04045) ? a/12.92 : powf((a+0.055)/1.055,2.4); | |
| 73 | } | |
| 74 | ||
| 75 | //---------------------------------------------------- | |
| 76 | //plain gamma (power function) forward (linear to gamma) | |
| 77 | //a = input, should be in the 0.0 to 1.0 range | |
| 78 | static inline float gamma_plain_f(float a, float g) | |
| 79 | { | |
| 80 | return powf(a,1.0/g); | |
| 81 | } | |
| 82 | ||
| 83 | //-------------------------------------------------------- | |
| 84 | //plain gamma (power function) backward (gamma to linear) | |
| 85 | //a = input, should be in the 0.0 to 1.0 range | |
| 86 | static inline float gamma_plain_b(float a, float g) | |
| 87 | { | |
| 88 | return powf(a,g); | |
| 89 | } | |
| 90 | ||
| 91 | //-------------------------------------------------------- | |
| 92 | //dummy function for linear tables (no gamma) | |
| 93 | //g = gamma value | |
| 94 | static inline float gamma_none(float a) | |
| 95 | { | |
| 96 | return a; | |
| 97 | } | |
| 98 | ||
| 99 | //------------------------------------------------ | |
| 100 | //expand highlights using a modified Spitzak formula | |
| 101 | //with limited max output value (250) | |
| 102 | //(for values up to 2500 use 1.0001 and 0.493) | |
| 103 | //input [0...1] | |
| 104 | //output [0...250] | |
| 105 | static inline float exp_hl(float a) | |
| 106 | { | |
| 107 | return (a<=0.4781) ? a : 0.25/(1.001-a); | |
| 108 | } | |
| 109 | ||
| 110 | //------------------------------------------------------------ | |
| 111 | //compress highlights using a modified Spitzak formula | |
| 112 | //with limited max input value (250) | |
| 113 | //(for values up to 2500 use 1.0001 and 0.493) | |
| 114 | //input [0...250] | |
| 115 | //output [0...1] | |
| 116 | static inline float cps_hl(float a) | |
| 117 | { | |
| 118 | return (a<=0.4781) ? a : 1.001-0.25/a; | |
| 119 | } | |
| 120 | ||
| 121 | //---------------------------------------------------------- | |
| 122 | //float to char conversion is done using the upper 16 bits | |
| 123 | //of the float number as an index into the conversion table. | |
| 124 | //This union is used for type punning, to avoid problems | |
| 125 | //with compiler optimizations, as the read access in the | |
| 126 | //cfc_tab_8 function directly follows writing | |
| 127 | typedef union | |
| 128 | { | |
| 129 | float a; | |
| 130 | uint16_t i[2]; | |
| 131 | } flint; | |
| 132 | ||
| 133 | //-------------------------------------------------------- | |
| 134 | //generate forward and backward conversion tables | |
| 135 | //for 8 bit (uint8_t) video | |
| 136 | //*ft = forward table (float to uchar, linear to gamma) | |
| 137 | // must have space for 65536 char elements | |
| 138 | //*bt = backward table (uchar to float, gamma to linear) | |
| 139 | // must have space for 256 float elements | |
| 140 | //type = what kind of gamma function will be used: | |
| 141 | // 0 = linear (no gamma, just multiply/divide by 255) | |
| 142 | // 1 = plain gamma (pure power function) | |
| 143 | // 2 = rec 709 gamma | |
| 144 | // 3 = sRGB gamma | |
| 145 | // 4 = sRGB gamma with highlight expansion/compression | |
| 146 | // types 0...3 map to [0...1] linear float range | |
| 147 | // type 4 maps to [0...250] linear float range | |
| 148 | //g = gamma value, 0.3 to 3.0 (only used with type=1) | |
| 149 | static inline void cfc_tab_8(uint8_t *ft, float *bt, int type, float g) | |
| 150 | { | |
| 151 | uint16_t i; | |
| 152 | float a; | |
| 153 | flint fi; | |
| 154 | ||
| 155 | // *** generate float to char conversion table *** | |
| 156 | for (i=0;i<128;i++) //positive denormals map to zero | |
| 157 | ft[i]=0; | |
| 158 | ||
| 159 | for (i=128;i<=32639;i++) //positive numbers map to 0...255 | |
| 160 | { | |
| 161 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 162 | // fi.i[0]=i; fi.i[1]=0x8000; //big endian | |
| 163 | //#endif | |
| 164 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 165 | fi.i[1]=i; fi.i[0]=0x8000; //little endian | |
| 166 | //#endif | |
| 167 | a=fi.a; | |
| 168 | switch (type) | |
| 169 | { | |
| 170 | case 0: a=gamma_none(a); break; | |
| 171 | case 1: a=gamma_plain_f(a,g); break; | |
| 172 | case 2: a=gamma_709_f(a); break; | |
| 173 | case 3: a=gamma_sRGB_f(a); break; | |
| 174 | case 4: a=cps_hl(a); a=gamma_sRGB_f(a); break; | |
| 175 | default: break; | |
| 176 | } | |
| 177 | if (a>0.9999) a=0.9999; | |
| 178 | ft[i]=(uint8_t)(256.0*a); | |
| 179 | } | |
| 180 | ||
| 181 | for (i=32640;i<32768;i++) //positive NANs and infinite map to 255 | |
| 182 | ft[i]=255; | |
| 183 | for (i=32768;i!=65535;i++) //everything negative maps to 0 | |
| 184 | ft[i]=0; | |
| 185 | ft[65535]=0; | |
| 186 | ||
| 187 | ||
| 188 | // *** generate char to float conversion table *** | |
| 189 | for (i=0;i<=255;i++) | |
| 190 | { | |
| 191 | a=((float)i+0.5)/256.0; | |
| 192 | switch (type) | |
| 193 | { | |
| 194 | case 0: a=gamma_none(a); break; | |
| 195 | case 1: a=gamma_plain_b(a,g); break; | |
| 196 | case 2: a=gamma_709_b(a); break; | |
| 197 | case 3: a=gamma_sRGB_b(a); break; | |
| 198 | case 4: a=gamma_sRGB_b(a); a=exp_hl(a); break; | |
| 199 | default: break; | |
| 200 | } | |
| 201 | bt[i]=a; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | //-------------------------------------------------------- | |
| 206 | //convert from paked uchar RGBA to packed float RGBA | |
| 207 | //w,h are width and height of the image | |
| 208 | //tab = table used for RGB conversion | |
| 209 | //atab = table used for alpha converion (usually linear) | |
| 210 | static inline void RGBA8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab, float *atab) | |
| 211 | { | |
| 212 | int i; | |
| 213 | uint8_t *cin=(uint8_t *)in; | |
| 214 | ||
| 215 | for (i=0;i<w*h;i++) | |
| 216 | { | |
| 217 | out[i].r=tab[*cin++]; | |
| 218 | out[i].g=tab[*cin++]; | |
| 219 | out[i].b=tab[*cin++]; | |
| 220 | out[i].a=atab[*cin++]; | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | //-------------------------------------------------------- | |
| 225 | //convert from paked uchar RGBA to packed float RGBA, | |
| 226 | //covert RGB only, SKIP ALPHA | |
| 227 | //w,h are width and height of the image | |
| 228 | //tab = table used for RGB conversion | |
| 229 | static inline void RGB8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab) | |
| 230 | { | |
| 231 | int i; | |
| 232 | uint8_t *cin=(uint8_t *)in; | |
| 233 | ||
| 234 | for (i=0;i<w*h;i++) | |
| 235 | { | |
| 236 | out[i].r=tab[*cin++]; | |
| 237 | out[i].g=tab[*cin++]; | |
| 238 | out[i].b=tab[*cin++]; | |
| 239 | cin++; | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | ||
| 244 | //-------------------------------------------------------- | |
| 245 | //in the following conversions, we can afford "direct" type punning | |
| 246 | //as the array already exists from well before (I hope :-) | |
| 247 | // | |
| 248 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 249 | //big endian most significant 16 bit word from 32bit float | |
| 250 | //#define MSWF(x) *(uint16_t*)x | |
| 251 | //#endif | |
| 252 | // | |
| 253 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 254 | //little endian most significant 16 bit word from 32bit float | |
| 255 | #define MSWF(x) *((uint16_t*)x+1) | |
| 256 | //#endif | |
| 257 | ||
| 258 | //---------------------------------------------------------- | |
| 259 | //convert from packed float RGBA to packed uchar RGBA | |
| 260 | //tab = table used for RGB conversion | |
| 261 | //atab = table used for alpha converion (usually linear) | |
| 262 | static inline void float_2_RGBA8(const float_rgba *in, uint32_t *out, int w, int h, uint8_t *tab, uint8_t *atab) | |
| 263 | { | |
| 264 | int i; | |
| 265 | uint8_t *cout=(uint8_t *)out; | |
| 266 | ||
| 267 | for (i=0;i<w*h;i++) | |
| 268 | { | |
| 269 | *cout++ = tab[MSWF(&in[i].r)]; | |
| 270 | *cout++ = tab[MSWF(&in[i].g)]; | |
| 271 | *cout++ = tab[MSWF(&in[i].b)]; | |
| 272 | *cout++ = atab[MSWF(&in[i].a)]; | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | //---------------------------------------------------------- | |
| 277 | //convert from packed float RGBA to packed uchar RGBA | |
| 278 | //covert RGB only, SKIP ALPHA | |
| 279 | //tab = table used for RGB conversion | |
| 280 | static inline void float_2_RGB8(const float_rgba *in, uint32_t *out, int w, int h, uint8_t *tab) | |
| 281 | { | |
| 282 | int i; | |
| 283 | uint8_t *cout=(uint8_t *)out; | |
| 284 | ||
| 285 | for (i=0;i<w*h;i++) | |
| 286 | { | |
| 287 | *cout++ = tab[MSWF(&in[i].r)]; | |
| 288 | *cout++ = tab[MSWF(&in[i].g)]; | |
| 289 | *cout++ = tab[MSWF(&in[i].b)]; | |
| 290 | cout++; | |
| 291 | } | |
| 292 | } | |
| 293 | ||
| 294 | #undef MSWF | |
| 295 | ||
| 296 | ||
| 297 | //-------------------------------------------------- | |
| 298 | // -- Single value conversion -- | |
| 299 | //there is no function for single uint8 to float, because | |
| 300 | //that is just a simple table lookup | |
| 301 | // | |
| 302 | //because float_2_uint8() can be called immediately after | |
| 303 | //the input value has been created, type punning has to be | |
| 304 | //done properly, with a union | |
| 305 | ||
| 306 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 307 | static inline uint8_t float_2_uint8(const float *in, uint8_t *tab) | |
| 308 | { | |
| 309 | return tab[((flint*)in)->i[1]]; | |
| 310 | } | |
| 311 | //endif | |
| 312 | ||
| 313 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 314 | //static inline float_2_uint8(const float *in, uint8_t *tab) | |
| 315 | //{ | |
| 316 | //return tab[((flint*)in)->i[0]]; | |
| 317 | //} | |
| 318 | //#endif |
| 0 | /* frei0r/colorspace.h | |
| 1 | * Copyright (C) 2004 Mathieu Guindon, Julien Keable, Jean-Sebastien Senecal | |
| 2 | * This file is part of Frei0r. | |
| 3 | * | |
| 4 | * This program is free software; you can redistribute it and/or modify | |
| 5 | * it under the terms of the GNU General Public License as published by | |
| 6 | * the Free Software Foundation; either version 2 of the License, or | |
| 7 | * (at your option) any later version. | |
| 8 | * | |
| 9 | * This program is distributed in the hope that it will be useful, | |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | * GNU General Public License for more details. | |
| 13 | * | |
| 14 | * You should have received a copy of the GNU General Public License | |
| 15 | * along with this program; if not, write to the Free Software | |
| 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 17 | */ | |
| 18 | ||
| 19 | #ifndef INCLUDED_FREI0R_COLORSPACE_H | |
| 20 | #define INCLUDED_FREI0R_COLORSPACE_H | |
| 21 | ||
| 22 | #include "frei0r/math.h" | |
| 23 | #include <stdlib.h> | |
| 24 | #include <math.h> | |
| 25 | ||
| 26 | // # Basic colorspace convert functions (from the Gimp gimpcolorspace.h) #### | |
| 27 | ||
| 28 | /* int functions */ | |
| 29 | ||
| 30 | /** | |
| 31 | * rgb_to_hsv_int | |
| 32 | * @red: The red channel value, returns the Hue channel | |
| 33 | * @green: The green channel value, returns the Saturation channel | |
| 34 | * @blue: The blue channel value, returns the Value channel | |
| 35 | * | |
| 36 | * The arguments are pointers to int representing channel values in | |
| 37 | * the RGB colorspace, and the values pointed to are all in the range | |
| 38 | * [0, 255]. | |
| 39 | * | |
| 40 | * The function changes the arguments to point to the HSV value | |
| 41 | * corresponding, with the returned values in the following | |
| 42 | * ranges: H [0, 360], S [0, 255], V [0, 255]. | |
| 43 | **/ | |
| 44 | inline void | |
| 45 | rgb_to_hsv_int (int *red /* returns hue */, | |
| 46 | int *green /* returns saturation */, | |
| 47 | int *blue /* returns value */) | |
| 48 | { | |
| 49 | double r, g, b; | |
| 50 | double h, s, v; | |
| 51 | double min; | |
| 52 | double delta; | |
| 53 | ||
| 54 | r = *red; | |
| 55 | g = *green; | |
| 56 | b = *blue; | |
| 57 | ||
| 58 | if (r > g) | |
| 59 | { | |
| 60 | v = MAX (r, b); | |
| 61 | min = MIN (g, b); | |
| 62 | } | |
| 63 | else | |
| 64 | { | |
| 65 | v = MAX (g, b); | |
| 66 | min = MIN (r, b); | |
| 67 | } | |
| 68 | ||
| 69 | delta = v - min; | |
| 70 | ||
| 71 | if (v == 0.0) | |
| 72 | s = 0.0; | |
| 73 | else | |
| 74 | s = delta / v; | |
| 75 | ||
| 76 | if (s == 0.0) | |
| 77 | h = 0.0; | |
| 78 | else | |
| 79 | { | |
| 80 | if (r == v) | |
| 81 | h = 60.0 * (g - b) / delta; | |
| 82 | else if (g == v) | |
| 83 | h = 120 + 60.0 * (b - r) / delta; | |
| 84 | else | |
| 85 | h = 240 + 60.0 * (r - g) / delta; | |
| 86 | ||
| 87 | if (h < 0.0) | |
| 88 | h += 360.0; | |
| 89 | if (h > 360.0) | |
| 90 | h -= 360.0; | |
| 91 | } | |
| 92 | ||
| 93 | *red = ROUND (h); | |
| 94 | *green = ROUND (s * 255.0); | |
| 95 | *blue = ROUND (v); | |
| 96 | } | |
| 97 | ||
| 98 | /** | |
| 99 | * hsv_to_rgb_int | |
| 100 | * @hue: The hue channel, returns the red channel | |
| 101 | * @saturation: The saturation channel, returns the green channel | |
| 102 | * @value: The value channel, returns the blue channel | |
| 103 | * | |
| 104 | * The arguments are pointers to int, with the values pointed to in the | |
| 105 | * following ranges: H [0, 360], S [0, 255], V [0, 255]. | |
| 106 | * | |
| 107 | * The function changes the arguments to point to the RGB value | |
| 108 | * corresponding, with the returned values all in the range [0, 255]. | |
| 109 | **/ | |
| 110 | inline void | |
| 111 | hsv_to_rgb_int (int *hue /* returns red */, | |
| 112 | int *saturation /* returns green */, | |
| 113 | int *value /* returns blue */) | |
| 114 | { | |
| 115 | double h, s, v, h_temp; | |
| 116 | double f, p, q, t; | |
| 117 | int i; | |
| 118 | ||
| 119 | if (*saturation == 0) | |
| 120 | { | |
| 121 | *hue = *value; | |
| 122 | *saturation = *value; | |
| 123 | // *value = *value; | |
| 124 | } | |
| 125 | else | |
| 126 | { | |
| 127 | h = *hue; | |
| 128 | s = *saturation / 255.0; | |
| 129 | v = *value / 255.0; | |
| 130 | ||
| 131 | if (h == 360) | |
| 132 | h_temp = 0; | |
| 133 | else | |
| 134 | h_temp = h; | |
| 135 | ||
| 136 | h_temp = h_temp / 60.0; | |
| 137 | i = (int) floor (h_temp); | |
| 138 | f = h_temp - i; | |
| 139 | p = v * (1.0 - s); | |
| 140 | q = v * (1.0 - (s * f)); | |
| 141 | t = v * (1.0 - (s * (1.0 - f))); | |
| 142 | ||
| 143 | switch (i) | |
| 144 | { | |
| 145 | case 0: | |
| 146 | *hue = ROUND (v * 255.0); | |
| 147 | *saturation = ROUND (t * 255.0); | |
| 148 | *value = ROUND (p * 255.0); | |
| 149 | break; | |
| 150 | ||
| 151 | case 1: | |
| 152 | *hue = ROUND (q * 255.0); | |
| 153 | *saturation = ROUND (v * 255.0); | |
| 154 | *value = ROUND (p * 255.0); | |
| 155 | break; | |
| 156 | ||
| 157 | case 2: | |
| 158 | *hue = ROUND (p * 255.0); | |
| 159 | *saturation = ROUND (v * 255.0); | |
| 160 | *value = ROUND (t * 255.0); | |
| 161 | break; | |
| 162 | ||
| 163 | case 3: | |
| 164 | *hue = ROUND (p * 255.0); | |
| 165 | *saturation = ROUND (q * 255.0); | |
| 166 | *value = ROUND (v * 255.0); | |
| 167 | break; | |
| 168 | ||
| 169 | case 4: | |
| 170 | *hue = ROUND (t * 255.0); | |
| 171 | *saturation = ROUND (p * 255.0); | |
| 172 | *value = ROUND (v * 255.0); | |
| 173 | break; | |
| 174 | ||
| 175 | case 5: | |
| 176 | *hue = ROUND (v * 255.0); | |
| 177 | *saturation = ROUND (p * 255.0); | |
| 178 | *value = ROUND (q * 255.0); | |
| 179 | break; | |
| 180 | } | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | /** | |
| 185 | * rgb_to_hsl_int | |
| 186 | * @red: Red channel, returns Hue channel | |
| 187 | * @green: Green channel, returns Lightness channel | |
| 188 | * @blue: Blue channel, returns Saturation channel | |
| 189 | * | |
| 190 | * The arguments are pointers to int representing channel values in the | |
| 191 | * RGB colorspace, and the values pointed to are all in the range [0, 255]. | |
| 192 | * | |
| 193 | * The function changes the arguments to point to the corresponding HLS | |
| 194 | * value with the values pointed to in the following ranges: H [0, 360], | |
| 195 | * L [0, 255], S [0, 255]. | |
| 196 | **/ | |
| 197 | inline void | |
| 198 | rgb_to_hsl_int (unsigned int *red /* returns red */, | |
| 199 | unsigned int *green /* returns green */, | |
| 200 | unsigned int *blue /* returns blue */) | |
| 201 | { | |
| 202 | unsigned int r, g, b; | |
| 203 | double h, s, l; | |
| 204 | unsigned int min, max; | |
| 205 | unsigned int delta; | |
| 206 | ||
| 207 | r = *red; | |
| 208 | g = *green; | |
| 209 | b = *blue; | |
| 210 | ||
| 211 | if (r > g) | |
| 212 | { | |
| 213 | max = MAX (r, b); | |
| 214 | min = MIN (g, b); | |
| 215 | } | |
| 216 | else | |
| 217 | { | |
| 218 | max = MAX (g, b); | |
| 219 | min = MIN (r, b); | |
| 220 | } | |
| 221 | ||
| 222 | l = (max + min) / 2.0; | |
| 223 | ||
| 224 | if (max == min) | |
| 225 | { | |
| 226 | s = 0.0; | |
| 227 | h = 0.0; | |
| 228 | } | |
| 229 | else | |
| 230 | { | |
| 231 | delta = (max - min); | |
| 232 | ||
| 233 | if (l < 128) | |
| 234 | s = 255 * (double) delta / (double) (max + min); | |
| 235 | else | |
| 236 | s = 255 * (double) delta / (double) (511 - max - min); | |
| 237 | ||
| 238 | if (r == max) | |
| 239 | h = (g - b) / (double) delta; | |
| 240 | else if (g == max) | |
| 241 | h = 2 + (b - r) / (double) delta; | |
| 242 | else | |
| 243 | h = 4 + (r - g) / (double) delta; | |
| 244 | ||
| 245 | h = h * 42.5; | |
| 246 | ||
| 247 | if (h < 0) | |
| 248 | h += 255; | |
| 249 | else if (h > 255) | |
| 250 | h -= 255; | |
| 251 | } | |
| 252 | ||
| 253 | *red = ROUND (h); | |
| 254 | *green = ROUND (s); | |
| 255 | *blue = ROUND (l); | |
| 256 | } | |
| 257 | ||
| 258 | inline int | |
| 259 | hsl_value_int (double n1, | |
| 260 | double n2, | |
| 261 | double hue) | |
| 262 | { | |
| 263 | double value; | |
| 264 | ||
| 265 | if (hue > 255) | |
| 266 | hue -= 255; | |
| 267 | else if (hue < 0) | |
| 268 | hue += 255; | |
| 269 | ||
| 270 | if (hue < 42.5) | |
| 271 | value = n1 + (n2 - n1) * (hue / 42.5); | |
| 272 | else if (hue < 127.5) | |
| 273 | value = n2; | |
| 274 | else if (hue < 170) | |
| 275 | value = n1 + (n2 - n1) * ((170 - hue) / 42.5); | |
| 276 | else | |
| 277 | value = n1; | |
| 278 | ||
| 279 | return ROUND (value * 255.0); | |
| 280 | } | |
| 281 | ||
| 282 | /** | |
| 283 | * hsl_to_rgb_int | |
| 284 | * @hue: Hue channel, returns Red channel | |
| 285 | * @saturation: Saturation channel, returns Green channel | |
| 286 | * @lightness: Lightness channel, returns Blue channel | |
| 287 | * | |
| 288 | * The arguments are pointers to int, with the values pointed to in the | |
| 289 | * following ranges: H [0, 360], L [0, 255], S [0, 255]. | |
| 290 | * | |
| 291 | * The function changes the arguments to point to the RGB value | |
| 292 | * corresponding, with the returned values all in the range [0, 255]. | |
| 293 | **/ | |
| 294 | inline void | |
| 295 | hsl_to_rgb_int (unsigned int *hue /* returns red */, | |
| 296 | unsigned int *saturation /* returns green */, | |
| 297 | unsigned int *lightness /* returns blue */) | |
| 298 | { | |
| 299 | double h, s, l; | |
| 300 | ||
| 301 | h = *hue; | |
| 302 | s = *saturation; | |
| 303 | l = *lightness; | |
| 304 | ||
| 305 | if (s == 0) | |
| 306 | { | |
| 307 | /* achromatic case */ | |
| 308 | *hue = (int)l; | |
| 309 | *lightness = (int)l; | |
| 310 | *saturation = (int)l; | |
| 311 | } | |
| 312 | else | |
| 313 | { | |
| 314 | double m1, m2; | |
| 315 | ||
| 316 | if (l < 128) | |
| 317 | m2 = (l * (255 + s)) / 65025.0; | |
| 318 | else | |
| 319 | m2 = (l + s - (l * s) / 255.0) / 255.0; | |
| 320 | ||
| 321 | m1 = (l / 127.5) - m2; | |
| 322 | ||
| 323 | /* chromatic case */ | |
| 324 | *hue = hsl_value_int (m1, m2, h + 85); | |
| 325 | *saturation = hsl_value_int (m1, m2, h); | |
| 326 | *lightness = hsl_value_int (m1, m2, h - 85); | |
| 327 | } | |
| 328 | } | |
| 329 | ||
| 330 | /** | |
| 331 | * gimp_rgb_to_cmyk_int: | |
| 332 | * @red: the red channel; returns the cyan value (0-255) | |
| 333 | * @green: the green channel; returns the magenta value (0-255) | |
| 334 | * @blue: the blue channel; returns the yellow value (0-255) | |
| 335 | * @pullout: the percentage of black to pull out (0-100); returns | |
| 336 | * the black value (0-255) | |
| 337 | * | |
| 338 | * Does a naive conversion from RGB to CMYK colorspace. A simple | |
| 339 | * formula that doesn't take any color-profiles into account is used. | |
| 340 | * The amount of black pullout how can be controlled via the @pullout | |
| 341 | * parameter. A @pullout value of 0 makes this a conversion to CMY. | |
| 342 | * A value of 100 causes the maximum amount of black to be pulled out. | |
| 343 | **/ | |
| 344 | inline void | |
| 345 | gimp_rgb_to_cmyk_int (int *red, | |
| 346 | int *green, | |
| 347 | int *blue, | |
| 348 | int *pullout) | |
| 349 | { | |
| 350 | int c, m, y; | |
| 351 | ||
| 352 | c = 255 - *red; | |
| 353 | m = 255 - *green; | |
| 354 | y = 255 - *blue; | |
| 355 | ||
| 356 | if (*pullout == 0) | |
| 357 | { | |
| 358 | *red = c; | |
| 359 | *green = m; | |
| 360 | *blue = y; | |
| 361 | } | |
| 362 | else | |
| 363 | { | |
| 364 | int k = 255; | |
| 365 | ||
| 366 | if (c < k) k = c; | |
| 367 | if (m < k) k = m; | |
| 368 | if (y < k) k = y; | |
| 369 | ||
| 370 | k = (k * CLAMP (*pullout, 0, 100)) / 100; | |
| 371 | ||
| 372 | *red = ((c - k) << 8) / (256 - k); | |
| 373 | *green = ((m - k) << 8) / (256 - k); | |
| 374 | *blue = ((y - k) << 8) / (256 - k); | |
| 375 | *pullout = k; | |
| 376 | } | |
| 377 | } | |
| 378 | ||
| 379 | /** | |
| 380 | * gimp_cmyk_to_rgb_int: | |
| 381 | * @cyan: the cyan channel; returns the red value (0-255) | |
| 382 | * @magenta: the magenta channel; returns the green value (0-255) | |
| 383 | * @yellow: the yellow channel; returns the blue value (0-255) | |
| 384 | * @black: the black channel (0-255); doesn't change | |
| 385 | * | |
| 386 | * Does a naive conversion from CMYK to RGB colorspace. A simple | |
| 387 | * formula that doesn't take any color-profiles into account is used. | |
| 388 | **/ | |
| 389 | inline void | |
| 390 | cmyk_to_rgb_int (int *cyan, | |
| 391 | int *magenta, | |
| 392 | int *yellow, | |
| 393 | int *black) | |
| 394 | { | |
| 395 | int c, m, y, k; | |
| 396 | ||
| 397 | c = *cyan; | |
| 398 | m = *magenta; | |
| 399 | y = *yellow; | |
| 400 | k = *black; | |
| 401 | ||
| 402 | if (k) | |
| 403 | { | |
| 404 | c = ((c * (256 - k)) >> 8) + k; | |
| 405 | m = ((m * (256 - k)) >> 8) + k; | |
| 406 | y = ((y * (256 - k)) >> 8) + k; | |
| 407 | } | |
| 408 | ||
| 409 | *cyan = 255 - c; | |
| 410 | *magenta = 255 - m; | |
| 411 | *yellow = 255 - y; | |
| 412 | } | |
| 413 | ||
| 414 | ||
| 415 | #endif |
| 0 | #ifndef INCLUDED_FREI0R_MATH_H | |
| 1 | #define INCLUDED_FREI0R_MATH_H | |
| 2 | ||
| 3 | /* | |
| 4 | ||
| 5 | Code stripped from The Gimp: | |
| 6 | INT_MULT(a,b,t) | |
| 7 | INT_MULT3(a,b,c,t) | |
| 8 | INT_BLEND(a,b,alpha,tmp) | |
| 9 | CLAMP | |
| 10 | ROUND | |
| 11 | MAX255 | |
| 12 | ||
| 13 | Code stripped from Drone: | |
| 14 | CLAMP0255 | |
| 15 | SQR | |
| 16 | */ | |
| 17 | ||
| 18 | /* Clamps a int32-range int between 0 and 255 inclusive. */ | |
| 19 | #ifndef CLAMP0255 | |
| 20 | static inline unsigned char CLAMP0255(int32_t a) | |
| 21 | { | |
| 22 | return (unsigned char) | |
| 23 | ( (((-a) >> 31) & a) // 0 if the number was negative | |
| 24 | | (255 - a) >> 31); // -1 if the number was greater than 255 | |
| 25 | } | |
| 26 | #endif | |
| 27 | ||
| 28 | /* Provided temporary int t, returns a * b / 255 */ | |
| 29 | #ifndef INT_MULT | |
| 30 | #define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) | |
| 31 | #endif | |
| 32 | ||
| 33 | /* This version of INT_MULT3 is very fast, but suffers from some | |
| 34 | slight roundoff errors. It returns the correct result 99.987 | |
| 35 | percent of the time */ | |
| 36 | #ifndef INT_MULT3 | |
| 37 | #define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \ | |
| 38 | ((((t) >> 7) + (t)) >> 16)) | |
| 39 | #endif | |
| 40 | ||
| 41 | #ifndef INT_BLEND | |
| 42 | #define INT_BLEND(a,b,alpha,tmp) (INT_MULT((a) - (b), alpha, tmp) + (b)) | |
| 43 | #endif | |
| 44 | ||
| 45 | #ifndef CLAMP | |
| 46 | //! Clamp x at min and max | |
| 47 | #define CLAMP(x,min,max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) | |
| 48 | #endif | |
| 49 | ||
| 50 | #ifndef ROUND | |
| 51 | //! Round. | |
| 52 | #define ROUND(x) ((int32_t)((x)+0.5)) | |
| 53 | #endif | |
| 54 | ||
| 55 | #ifndef SQR | |
| 56 | //! Square. | |
| 57 | #define SQR(x) ((x) * (x)) | |
| 58 | #endif | |
| 59 | ||
| 60 | #ifndef MAX255 | |
| 61 | //! Limit a (0->511) int to 255. | |
| 62 | uint8_t MAX255(uint32_t a) { return (uint8_t) (a | ((a & 256) - ((a & 256) >> 8))); } | |
| 63 | #endif | |
| 64 | ||
| 65 | #ifndef MIN | |
| 66 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
| 67 | #endif | |
| 68 | ||
| 69 | #ifndef MAX | |
| 70 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) | |
| 71 | #endif | |
| 72 | ||
| 73 | #endif |
| 100 | 100 | { |
| 101 | 101 | case F0R_PARAM_BOOL : |
| 102 | 102 | *static_cast<f0r_param_bool*>(param) |
| 103 | = *static_cast<f0r_param_bool*>(ptr) > 0.5 ? 1.0 : 0.0; | |
| 103 | = *static_cast<bool*>(ptr) ? 1.0 : 0.0; | |
| 104 | 104 | break; |
| 105 | 105 | case F0R_PARAM_DOUBLE: |
| 106 | 106 | *static_cast<f0r_param_double*>(param) |
| 0 | /* | |
| 1 | * frei0r_cairo.h | |
| 2 | * Copyright 2012 Janne Liljeblad | |
| 3 | * | |
| 4 | * This file is part of Frei0r. | |
| 5 | * | |
| 6 | * This program is free software; you can redistribute it and/or modify | |
| 7 | * it under the terms of the GNU General Public License as published by | |
| 8 | * the Free Software Foundation; either version 2 of the License, or | |
| 9 | * (at your option) any later version. | |
| 10 | * | |
| 11 | * This program is distributed in the hope that it will be useful, | |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | * GNU General Public License for more details. | |
| 15 | * | |
| 16 | * You should have received a copy of the GNU General Public License | |
| 17 | * along with this program; if not, write to the Free Software | |
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 19 | */ | |
| 20 | ||
| 21 | ||
| 22 | #include <cairo.h> | |
| 23 | #include <string.h> | |
| 24 | #include "frei0r_math.h" | |
| 25 | ||
| 26 | /** | |
| 27 | * String identifiers for gradient types available using Cairo. | |
| 28 | */ | |
| 29 | #define GRADIENT_LINEAR "gradient_linear" | |
| 30 | #define GRADIENT_RADIAL "gradient_radial" | |
| 31 | ||
| 32 | /** | |
| 33 | * String identifiers for blend modes available using Cairo. | |
| 34 | */ | |
| 35 | #define NORMAL "normal" | |
| 36 | #define ADD "add" | |
| 37 | #define SATURATE "saturate" | |
| 38 | #define MULTIPLY "multiply" | |
| 39 | #define SCREEN "screen" | |
| 40 | #define OVERLAY "overlay" | |
| 41 | #define DARKEN "darken" | |
| 42 | #define LIGHTEN "lighten" | |
| 43 | #define COLORDODGE "colordodge" | |
| 44 | #define COLORBURN "colorburn" | |
| 45 | #define HARDLIGHT "hardlight" | |
| 46 | #define SOFTLIGHT "softlight" | |
| 47 | #define DIFFERENCE "difference" | |
| 48 | #define EXCLUSION "exclusion" | |
| 49 | #define HSLHUE "hslhue" | |
| 50 | #define HSLSATURATION "hslsaturation" | |
| 51 | #define HSLCOLOR "hslcolor" | |
| 52 | #define HSLLUMINOSITY "hslluminosity" | |
| 53 | ||
| 54 | ||
| 55 | /** | |
| 56 | * frei0r_cairo_set_operator | |
| 57 | * @cr: Cairo context | |
| 58 | * @op: String identifier for a blend mode | |
| 59 | * | |
| 60 | * Sets cairo context to use the defined blend mode for all paint operations. | |
| 61 | */ | |
| 62 | void frei0r_cairo_set_operator(cairo_t *cr, char *op) | |
| 63 | { | |
| 64 | if(strcmp(op, NORMAL) == 0) | |
| 65 | { | |
| 66 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 67 | } | |
| 68 | else if(strcmp(op, ADD) == 0) | |
| 69 | { | |
| 70 | cairo_set_operator (cr, CAIRO_OPERATOR_ADD); | |
| 71 | } | |
| 72 | else if(strcmp(op, SATURATE) == 0) | |
| 73 | { | |
| 74 | cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); | |
| 75 | } | |
| 76 | else if(strcmp(op, MULTIPLY) == 0) | |
| 77 | { | |
| 78 | cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY); | |
| 79 | } | |
| 80 | else if(strcmp(op, SCREEN) == 0) | |
| 81 | { | |
| 82 | cairo_set_operator (cr, CAIRO_OPERATOR_SCREEN); | |
| 83 | } | |
| 84 | else if(strcmp(op, OVERLAY) == 0) | |
| 85 | { | |
| 86 | cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY); | |
| 87 | } | |
| 88 | else if(strcmp(op, DARKEN) == 0) | |
| 89 | { | |
| 90 | cairo_set_operator (cr, CAIRO_OPERATOR_DARKEN); | |
| 91 | } | |
| 92 | else if(strcmp(op, LIGHTEN) == 0) | |
| 93 | { | |
| 94 | cairo_set_operator (cr, CAIRO_OPERATOR_LIGHTEN); | |
| 95 | } | |
| 96 | else if(strcmp(op, COLORDODGE) == 0) | |
| 97 | { | |
| 98 | cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE); | |
| 99 | } | |
| 100 | else if(strcmp(op, COLORBURN) == 0) | |
| 101 | { | |
| 102 | cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_BURN); | |
| 103 | } | |
| 104 | else if(strcmp(op, HARDLIGHT) == 0) | |
| 105 | { | |
| 106 | cairo_set_operator (cr, CAIRO_OPERATOR_HARD_LIGHT); | |
| 107 | } | |
| 108 | else if(strcmp(op, SOFTLIGHT) == 0) | |
| 109 | { | |
| 110 | cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT); | |
| 111 | } | |
| 112 | else if(strcmp(op, DIFFERENCE) == 0) | |
| 113 | { | |
| 114 | cairo_set_operator (cr, CAIRO_OPERATOR_DIFFERENCE); | |
| 115 | } | |
| 116 | else if(strcmp(op, EXCLUSION) == 0) | |
| 117 | { | |
| 118 | cairo_set_operator (cr, CAIRO_OPERATOR_EXCLUSION); | |
| 119 | } | |
| 120 | else if(strcmp(op, HSLHUE) == 0) | |
| 121 | { | |
| 122 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_HUE); | |
| 123 | } | |
| 124 | else if(strcmp(op, HSLSATURATION) == 0) | |
| 125 | { | |
| 126 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION); | |
| 127 | } | |
| 128 | else if(strcmp(op, HSLCOLOR) == 0) | |
| 129 | { | |
| 130 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_COLOR); | |
| 131 | } | |
| 132 | else if(strcmp(op, HSLLUMINOSITY ) == 0) | |
| 133 | { | |
| 134 | cairo_set_operator (cr, CAIRO_OPERATOR_HSL_LUMINOSITY); | |
| 135 | } | |
| 136 | else | |
| 137 | { | |
| 138 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | ||
| 143 | /** | |
| 144 | * frei0r_cairo_set_rgba_LITTLE_ENDIAN | |
| 145 | * @cr: Cairo context | |
| 146 | * @red: red component, 0 - 1 | |
| 147 | * @green: green component, 0 - 1 | |
| 148 | * @blue: blue component, 0 - 1 | |
| 149 | * @alpha: opacity of color, 0 -1 | |
| 150 | * | |
| 151 | * Sets cairo context to use the defined color paint operations. | |
| 152 | * Switches red and blue channels to get correct color on little endian machines. | |
| 153 | * This method only works correctly on little endian machines. | |
| 154 | */ | |
| 155 | void frei0r_cairo_set_rgba_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue, double alpha) | |
| 156 | { | |
| 157 | cairo_set_source_rgba (cr, blue, green, red, alpha); | |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * frei0r_cairo_set_rgb_LITTLE_ENDIAN | |
| 162 | * @cr: Cairo context | |
| 163 | * @red: red component, 0 - 1 | |
| 164 | * @green: green component, 0 - 1 | |
| 165 | * @blue: blue component, 0 - 1 | |
| 166 | * | |
| 167 | * Sets cairo context to use the defined color paint operations. | |
| 168 | * Switches red and blue channels to get correct color on little endian machines. | |
| 169 | * This method only works correctly on little endian machines. | |
| 170 | */ | |
| 171 | void frei0r_cairo_set_rgb_LITTLE_ENDIAN(cairo_t* cr, double red, double green, double blue) | |
| 172 | { | |
| 173 | cairo_set_source_rgb (cr, blue, green, red); | |
| 174 | } | |
| 175 | ||
| 176 | /** | |
| 177 | * freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN( | |
| 178 | * @pat: Cairo pattern | |
| 179 | * @offset: offset of color position in pattern space, 0 - 1 | |
| 180 | * @red: red component, 0 - 1 | |
| 181 | * @green: green component, 0 - 1 | |
| 182 | * @blue: blue component, 0 - 1 | |
| 183 | * @alpha: opacity of color, 0 -1 | |
| 184 | * | |
| 185 | * Sets color stop for cairo patterns. | |
| 186 | * Switches red and blue channels to get correct color on little endian machines. | |
| 187 | * This method only works correctly on little endian machines. | |
| 188 | */ | |
| 189 | void freior_cairo_set_color_stop_rgba_LITTLE_ENDIAN(cairo_pattern_t *pat, double offset, | |
| 190 | double red, double green, double blue, double alpha) | |
| 191 | { | |
| 192 | cairo_pattern_add_color_stop_rgba (pat, offset, blue, green, red, alpha); | |
| 193 | } | |
| 194 | ||
| 195 | /** | |
| 196 | * frei0r_cairo_get_pixel_position | |
| 197 | * @norm_pos: position in range 0 - 1, either x or y | |
| 198 | * @dim: dimension, either witdh or height | |
| 199 | * | |
| 200 | * Converts double range [0 -> 1] to pixel range [-2*dim -> 3*dim]. Input 0.4 gives position 0. | |
| 201 | * | |
| 202 | * Returns: position in pixels | |
| 203 | */ | |
| 204 | double frei0r_cairo_get_pixel_position (double norm_pos, int dim) | |
| 205 | { | |
| 206 | double pos_o = -(dim * 2.0); | |
| 207 | return pos_o + norm_pos * dim * 5.0; | |
| 208 | } | |
| 209 | ||
| 210 | /** | |
| 211 | * frei0r_cairo_get_scale | |
| 212 | * @norm_scale: scale in range 0 - 1 | |
| 213 | * | |
| 214 | * Converts double range [0 -> 1] to scale range [0 -> 5]. Input 0.2 gives scale 1.0. | |
| 215 | * | |
| 216 | * Returns: scale | |
| 217 | */ | |
| 218 | double frei0r_cairo_get_scale (double norm_scale) | |
| 219 | { | |
| 220 | return norm_scale * 5.0; | |
| 221 | } | |
| 222 | ||
| 223 | /** | |
| 224 | * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. | |
| 225 | * | |
| 226 | * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 | |
| 227 | * \param pixels the size of the image buffer in number of pixels | |
| 228 | * \param alpha if >= 0, the alpha channel will be set to this value | |
| 229 | * \see frei0r_cairo_unpremultiply_rgba | |
| 230 | */ | |
| 231 | void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) | |
| 232 | { | |
| 233 | int i = pixels + 1; | |
| 234 | while ( --i ) { | |
| 235 | register unsigned char a = rgba[3]; | |
| 236 | if (a == 0) { | |
| 237 | *((uint32_t *)rgba) = 0; | |
| 238 | } else if (a < 0xff) { | |
| 239 | rgba[0] = ( rgba[0] * a ) >> 8; | |
| 240 | rgba[1] = ( rgba[1] * a ) >> 8; | |
| 241 | rgba[2] = ( rgba[2] * a ) >> 8; | |
| 242 | } | |
| 243 | if (alpha >= 0) rgba[3] = alpha; | |
| 244 | rgba += 4; | |
| 245 | } | |
| 246 | } | |
| 247 | ||
| 248 | /** | |
| 249 | * Convert Cairo ARGB pre-multiplied alpha to frei0r straight RGBA. | |
| 250 | * | |
| 251 | * \param rgba the image buffer with format CAIRO_FORMAT_ARGB32 | |
| 252 | * \param pixels the size of the image buffer in number of pixels | |
| 253 | * \see frei0r_cairo_premultiply_rgba | |
| 254 | */ | |
| 255 | void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) | |
| 256 | { | |
| 257 | int i = pixels + 1; | |
| 258 | while ( --i ) { | |
| 259 | register unsigned char a = rgba[3]; | |
| 260 | if (a > 0 && a < 0xff) { | |
| 261 | rgba[0] = MIN(( rgba[0] << 8 ) / a, 255); | |
| 262 | rgba[1] = MIN(( rgba[1] << 8 ) / a, 255); | |
| 263 | rgba[2] = MIN(( rgba[2] << 8 ) / a, 255); | |
| 264 | } | |
| 265 | rgba += 4; | |
| 266 | } | |
| 267 | } | |
| 268 | ||
| 269 | /** | |
| 270 | * Convert frei0r RGBA to pre-multiplied alpha as needed by Cairo. | |
| 271 | * | |
| 272 | * \param rgba the image buffer with format F0R_COLOR_MODEL_RGBA8888 | |
| 273 | * \param pixels the size of the image buffer in number of pixels | |
| 274 | * \param alpha if >= 0, the alpha channel will be set to this value | |
| 275 | * \see frei0r_cairo_premultiply_rgba | |
| 276 | * | |
| 277 | * This is the same as frei0r_cairo_premultiply_rgba but it writes the | |
| 278 | * output to a different buffer. | |
| 279 | */ | |
| 280 | void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, | |
| 281 | int pixels, int alpha) | |
| 282 | { | |
| 283 | int i = pixels + 1; | |
| 284 | while ( --i ) { | |
| 285 | register unsigned char a = in[3]; | |
| 286 | if (a == 0) { | |
| 287 | *((uint32_t *)out) = 0; | |
| 288 | } else if (a == 0xff) { | |
| 289 | memcpy(out, in, 4); | |
| 290 | } else { | |
| 291 | out[0] = ( in[0] * a ) >> 8; | |
| 292 | out[1] = ( in[1] * a ) >> 8; | |
| 293 | out[2] = ( in[2] * a ) >> 8; | |
| 294 | if (alpha < 0) | |
| 295 | out[3] = a; | |
| 296 | } | |
| 297 | if (alpha >= 0) | |
| 298 | out[3] = alpha; | |
| 299 | in += 4; | |
| 300 | out += 4; | |
| 301 | } | |
| 302 | } |
| 0 | /* cfc.h | |
| 1 | * uchar->float and float->uchar conversion for packed RGBA video | |
| 2 | * with flexible gamma correction | |
| 3 | * | |
| 4 | * Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu | |
| 5 | * This file is a part of the Frei0r package | |
| 6 | * | |
| 7 | * This program is free software; you can redistribute it and/or modify | |
| 8 | * it under the terms of the GNU General Public License as published by | |
| 9 | * the Free Software Foundation; either version 2 of the License, or | |
| 10 | * (at your option) any later version. | |
| 11 | * | |
| 12 | * This program is distributed in the hope that it will be useful, | |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 | * GNU General Public License for more details. | |
| 16 | * | |
| 17 | * You should have received a copy of the GNU General Public License | |
| 18 | * along with this program; if not, write to the Free Software | |
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 20 | */ | |
| 21 | ||
| 22 | // the float to uint8_t conversion is done | |
| 23 | // using Spitzak type tables (upper 16 bits | |
| 24 | // of a float value used as table index) | |
| 25 | // see http://mysite.verizon.net/spitzak/conversion/ | |
| 26 | ||
| 27 | #include <math.h> | |
| 28 | #include <inttypes.h> | |
| 29 | ||
| 30 | ||
| 31 | typedef struct | |
| 32 | { | |
| 33 | float r; | |
| 34 | float g; | |
| 35 | float b; | |
| 36 | float a; | |
| 37 | } float_rgba; | |
| 38 | ||
| 39 | //------------------------------------------------------ | |
| 40 | //the following gamma functions need not be speed optimized, | |
| 41 | //as they are only used for table generation | |
| 42 | ||
| 43 | //-------------------------------------------------------- | |
| 44 | //rec 709 gamma forward (linear to gamma) | |
| 45 | //a = input, should be in the 0.0 to 1.0 range | |
| 46 | static inline float gamma_709_f(float a) | |
| 47 | { | |
| 48 | return (a<=0.018) ? 4.5*a : 1.099*powf(a,0.45)-0.099; | |
| 49 | } | |
| 50 | ||
| 51 | //-------------------------------------------------------- | |
| 52 | //rec 709 gamma backward (gamma to linear) | |
| 53 | //a = input, should be in the 0.0 to 1.0 range | |
| 54 | static inline float gamma_709_b(float a) | |
| 55 | { | |
| 56 | return (a<=0.081) ? a/4.5 : powf((a+0.099)/1.099,1.0/0.45); | |
| 57 | } | |
| 58 | ||
| 59 | //---------------------------------------------------- | |
| 60 | //sRGB gamma forward (linear to gamma) | |
| 61 | //a = input, should be in the 0.0 to 1.0 range | |
| 62 | static inline float gamma_sRGB_f(float a) | |
| 63 | { | |
| 64 | return (a<=0.0031308) ? 12.92*a : 1.055*powf(a,1.0/2.4)-0.055; | |
| 65 | } | |
| 66 | ||
| 67 | //-------------------------------------------------------- | |
| 68 | //sRGB gamma backward (gamma to linear) | |
| 69 | //a = input, should be in the 0.0 to 1.0 range | |
| 70 | static inline float gamma_sRGB_b(float a) | |
| 71 | { | |
| 72 | return (a<=0.04045) ? a/12.92 : powf((a+0.055)/1.055,2.4); | |
| 73 | } | |
| 74 | ||
| 75 | //---------------------------------------------------- | |
| 76 | //plain gamma (power function) forward (linear to gamma) | |
| 77 | //a = input, should be in the 0.0 to 1.0 range | |
| 78 | static inline float gamma_plain_f(float a, float g) | |
| 79 | { | |
| 80 | return powf(a,1.0/g); | |
| 81 | } | |
| 82 | ||
| 83 | //-------------------------------------------------------- | |
| 84 | //plain gamma (power function) backward (gamma to linear) | |
| 85 | //a = input, should be in the 0.0 to 1.0 range | |
| 86 | static inline float gamma_plain_b(float a, float g) | |
| 87 | { | |
| 88 | return powf(a,g); | |
| 89 | } | |
| 90 | ||
| 91 | //-------------------------------------------------------- | |
| 92 | //dummy function for linear tables (no gamma) | |
| 93 | //g = gamma value | |
| 94 | static inline float gamma_none(float a) | |
| 95 | { | |
| 96 | return a; | |
| 97 | } | |
| 98 | ||
| 99 | //------------------------------------------------ | |
| 100 | //expand highlights using a modified Spitzak formula | |
| 101 | //with limited max output value (250) | |
| 102 | //(for values up to 2500 use 1.0001 and 0.493) | |
| 103 | //input [0...1] | |
| 104 | //output [0...250] | |
| 105 | static inline float exp_hl(float a) | |
| 106 | { | |
| 107 | return (a<=0.4781) ? a : 0.25/(1.001-a); | |
| 108 | } | |
| 109 | ||
| 110 | //------------------------------------------------------------ | |
| 111 | //compress highlights using a modified Spitzak formula | |
| 112 | //with limited max input value (250) | |
| 113 | //(for values up to 2500 use 1.0001 and 0.493) | |
| 114 | //input [0...250] | |
| 115 | //output [0...1] | |
| 116 | static inline float cps_hl(float a) | |
| 117 | { | |
| 118 | return (a<=0.4781) ? a : 1.001-0.25/a; | |
| 119 | } | |
| 120 | ||
| 121 | //---------------------------------------------------------- | |
| 122 | //float to char conversion is done using the upper 16 bits | |
| 123 | //of the float number as an index into the conversion table. | |
| 124 | //This union is used for type punning, to avoid problems | |
| 125 | //with compiler optimizations, as the read access in the | |
| 126 | //cfc_tab_8 function directly follows writing | |
| 127 | typedef union | |
| 128 | { | |
| 129 | float a; | |
| 130 | uint16_t i[2]; | |
| 131 | } flint; | |
| 132 | ||
| 133 | //-------------------------------------------------------- | |
| 134 | //generate forward and backward conversion tables | |
| 135 | //for 8 bit (uint8_t) video | |
| 136 | //*ft = forward table (float to uchar, linear to gamma) | |
| 137 | // must have space for 65536 char elements | |
| 138 | //*bt = backward table (uchar to float, gamma to linear) | |
| 139 | // must have space for 256 float elements | |
| 140 | //type = what kind of gamma function will be used: | |
| 141 | // 0 = linear (no gamma, just multiply/divide by 255) | |
| 142 | // 1 = plain gamma (pure power function) | |
| 143 | // 2 = rec 709 gamma | |
| 144 | // 3 = sRGB gamma | |
| 145 | // 4 = sRGB gamma with highlight expansion/compression | |
| 146 | // types 0...3 map to [0...1] linear float range | |
| 147 | // type 4 maps to [0...250] linear float range | |
| 148 | //g = gamma value, 0.3 to 3.0 (only used with type=1) | |
| 149 | static inline void cfc_tab_8(uint8_t *ft, float *bt, int type, float g) | |
| 150 | { | |
| 151 | uint16_t i; | |
| 152 | float a; | |
| 153 | flint fi; | |
| 154 | ||
| 155 | // *** generate float to char conversion table *** | |
| 156 | for (i=0;i<128;i++) //positive denormals map to zero | |
| 157 | ft[i]=0; | |
| 158 | ||
| 159 | for (i=128;i<=32639;i++) //positive numbers map to 0...255 | |
| 160 | { | |
| 161 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 162 | // fi.i[0]=i; fi.i[1]=0x8000; //big endian | |
| 163 | //#endif | |
| 164 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 165 | fi.i[1]=i; fi.i[0]=0x8000; //little endian | |
| 166 | //#endif | |
| 167 | a=fi.a; | |
| 168 | switch (type) | |
| 169 | { | |
| 170 | case 0: a=gamma_none(a); break; | |
| 171 | case 1: a=gamma_plain_f(a,g); break; | |
| 172 | case 2: a=gamma_709_f(a); break; | |
| 173 | case 3: a=gamma_sRGB_f(a); break; | |
| 174 | case 4: a=cps_hl(a); a=gamma_sRGB_f(a); break; | |
| 175 | default: break; | |
| 176 | } | |
| 177 | if (a>0.9999) a=0.9999; | |
| 178 | ft[i]=(uint8_t)(256.0*a); | |
| 179 | } | |
| 180 | ||
| 181 | for (i=32640;i<32768;i++) //positive NANs and infinite map to 255 | |
| 182 | ft[i]=255; | |
| 183 | for (i=32768;i!=65535;i++) //everything negative maps to 0 | |
| 184 | ft[i]=0; | |
| 185 | ft[65535]=0; | |
| 186 | ||
| 187 | ||
| 188 | // *** generate char to float conversion table *** | |
| 189 | for (i=0;i<=255;i++) | |
| 190 | { | |
| 191 | a=((float)i+0.5)/256.0; | |
| 192 | switch (type) | |
| 193 | { | |
| 194 | case 0: a=gamma_none(a); break; | |
| 195 | case 1: a=gamma_plain_b(a,g); break; | |
| 196 | case 2: a=gamma_709_b(a); break; | |
| 197 | case 3: a=gamma_sRGB_b(a); break; | |
| 198 | case 4: a=gamma_sRGB_b(a); a=exp_hl(a); break; | |
| 199 | default: break; | |
| 200 | } | |
| 201 | bt[i]=a; | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | //-------------------------------------------------------- | |
| 206 | //convert from paked uchar RGBA to packed float RGBA | |
| 207 | //w,h are width and height of the image | |
| 208 | //tab = table used for RGB conversion | |
| 209 | //atab = table used for alpha converion (usually linear) | |
| 210 | static inline void RGBA8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab, float *atab) | |
| 211 | { | |
| 212 | int i; | |
| 213 | uint8_t *cin=(uint8_t *)in; | |
| 214 | ||
| 215 | for (i=0;i<w*h;i++) | |
| 216 | { | |
| 217 | out[i].r=tab[*cin++]; | |
| 218 | out[i].g=tab[*cin++]; | |
| 219 | out[i].b=tab[*cin++]; | |
| 220 | out[i].a=atab[*cin++]; | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | //-------------------------------------------------------- | |
| 225 | //convert from paked uchar RGBA to packed float RGBA, | |
| 226 | //covert RGB only, SKIP ALPHA | |
| 227 | //w,h are width and height of the image | |
| 228 | //tab = table used for RGB conversion | |
| 229 | static inline void RGB8_2_float(const uint32_t *in, float_rgba *out, int w, int h, float *tab) | |
| 230 | { | |
| 231 | int i; | |
| 232 | uint8_t *cin=(uint8_t *)in; | |
| 233 | ||
| 234 | for (i=0;i<w*h;i++) | |
| 235 | { | |
| 236 | out[i].r=tab[*cin++]; | |
| 237 | out[i].g=tab[*cin++]; | |
| 238 | out[i].b=tab[*cin++]; | |
| 239 | cin++; | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | ||
| 244 | //-------------------------------------------------------- | |
| 245 | //in the following conversions, we can afford "direct" type punning | |
| 246 | //as the array already exists from well before (I hope :-) | |
| 247 | // | |
| 248 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 249 | //big endian most significant 16 bit word from 32bit float | |
| 250 | //#define MSWF(x) *(uint16_t*)x | |
| 251 | //#endif | |
| 252 | // | |
| 253 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 254 | //little endian most significant 16 bit word from 32bit float | |
| 255 | #define MSWF(x) *((uint16_t*)x+1) | |
| 256 | //#endif | |
| 257 | ||
| 258 | //---------------------------------------------------------- | |
| 259 | //convert from packed float RGBA to packed uchar RGBA | |
| 260 | //tab = table used for RGB conversion | |
| 261 | //atab = table used for alpha converion (usually linear) | |
| 262 | static inline void float_2_RGBA8(const float_rgba *in, uint32_t *out, int w, int h, uint8_t *tab, uint8_t *atab) | |
| 263 | { | |
| 264 | int i; | |
| 265 | uint8_t *cout=(uint8_t *)out; | |
| 266 | ||
| 267 | for (i=0;i<w*h;i++) | |
| 268 | { | |
| 269 | *cout++ = tab[MSWF(&in[i].r)]; | |
| 270 | *cout++ = tab[MSWF(&in[i].g)]; | |
| 271 | *cout++ = tab[MSWF(&in[i].b)]; | |
| 272 | *cout++ = atab[MSWF(&in[i].a)]; | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | //---------------------------------------------------------- | |
| 277 | //convert from packed float RGBA to packed uchar RGBA | |
| 278 | //covert RGB only, SKIP ALPHA | |
| 279 | //tab = table used for RGB conversion | |
| 280 | static inline void float_2_RGB8(const float_rgba *in, uint32_t *out, int w, int h, uint8_t *tab) | |
| 281 | { | |
| 282 | int i; | |
| 283 | uint8_t *cout=(uint8_t *)out; | |
| 284 | ||
| 285 | for (i=0;i<w*h;i++) | |
| 286 | { | |
| 287 | *cout++ = tab[MSWF(&in[i].r)]; | |
| 288 | *cout++ = tab[MSWF(&in[i].g)]; | |
| 289 | *cout++ = tab[MSWF(&in[i].b)]; | |
| 290 | cout++; | |
| 291 | } | |
| 292 | } | |
| 293 | ||
| 294 | #undef MSWF | |
| 295 | ||
| 296 | ||
| 297 | //-------------------------------------------------- | |
| 298 | // -- Single value conversion -- | |
| 299 | //there is no function for single uint8 to float, because | |
| 300 | //that is just a simple table lookup | |
| 301 | // | |
| 302 | //because float_2_uint8() can be called immediately after | |
| 303 | //the input value has been created, type punning has to be | |
| 304 | //done properly, with a union | |
| 305 | ||
| 306 | //#if FREI0R_BYTE_ORDER == FREI0R_LITTLE_ENDIAN | |
| 307 | static inline uint8_t float_2_uint8(const float *in, uint8_t *tab) | |
| 308 | { | |
| 309 | return tab[((flint*)in)->i[1]]; | |
| 310 | } | |
| 311 | //endif | |
| 312 | ||
| 313 | //#if FREI0R_BYTE_ORDER == FREI0R_BIG_ENDIAN | |
| 314 | //static inline float_2_uint8(const float *in, uint8_t *tab) | |
| 315 | //{ | |
| 316 | //return tab[((flint*)in)->i[0]]; | |
| 317 | //} | |
| 318 | //#endif |
| 0 | /* frei0r_colorspace.h | |
| 1 | * Copyright (C) 2004 Mathieu Guindon, Julien Keable, Jean-Sebastien Senecal | |
| 2 | * This file is part of Frei0r. | |
| 3 | * | |
| 4 | * This program is free software; you can redistribute it and/or modify | |
| 5 | * it under the terms of the GNU General Public License as published by | |
| 6 | * the Free Software Foundation; either version 2 of the License, or | |
| 7 | * (at your option) any later version. | |
| 8 | * | |
| 9 | * This program is distributed in the hope that it will be useful, | |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | * GNU General Public License for more details. | |
| 13 | * | |
| 14 | * You should have received a copy of the GNU General Public License | |
| 15 | * along with this program; if not, write to the Free Software | |
| 16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 17 | */ | |
| 18 | ||
| 19 | #ifndef INCLUDED_FREI0R_COLORSPACE_H | |
| 20 | #define INCLUDED_FREI0R_COLORSPACE_H | |
| 21 | ||
| 22 | #include "frei0r_math.h" | |
| 23 | #include <stdlib.h> | |
| 24 | #include <math.h> | |
| 25 | ||
| 26 | // # Basic colorspace convert functions (from the Gimp gimpcolorspace.h) #### | |
| 27 | ||
| 28 | /* int functions */ | |
| 29 | ||
| 30 | /** | |
| 31 | * rgb_to_hsv_int | |
| 32 | * @red: The red channel value, returns the Hue channel | |
| 33 | * @green: The green channel value, returns the Saturation channel | |
| 34 | * @blue: The blue channel value, returns the Value channel | |
| 35 | * | |
| 36 | * The arguments are pointers to int representing channel values in | |
| 37 | * the RGB colorspace, and the values pointed to are all in the range | |
| 38 | * [0, 255]. | |
| 39 | * | |
| 40 | * The function changes the arguments to point to the HSV value | |
| 41 | * corresponding, with the returned values in the following | |
| 42 | * ranges: H [0, 360], S [0, 255], V [0, 255]. | |
| 43 | **/ | |
| 44 | inline void | |
| 45 | rgb_to_hsv_int (int *red /* returns hue */, | |
| 46 | int *green /* returns saturation */, | |
| 47 | int *blue /* returns value */) | |
| 48 | { | |
| 49 | double r, g, b; | |
| 50 | double h, s, v; | |
| 51 | double min; | |
| 52 | double delta; | |
| 53 | ||
| 54 | r = *red; | |
| 55 | g = *green; | |
| 56 | b = *blue; | |
| 57 | ||
| 58 | if (r > g) | |
| 59 | { | |
| 60 | v = MAX (r, b); | |
| 61 | min = MIN (g, b); | |
| 62 | } | |
| 63 | else | |
| 64 | { | |
| 65 | v = MAX (g, b); | |
| 66 | min = MIN (r, b); | |
| 67 | } | |
| 68 | ||
| 69 | delta = v - min; | |
| 70 | ||
| 71 | if (v == 0.0) | |
| 72 | s = 0.0; | |
| 73 | else | |
| 74 | s = delta / v; | |
| 75 | ||
| 76 | if (s == 0.0) | |
| 77 | h = 0.0; | |
| 78 | else | |
| 79 | { | |
| 80 | if (r == v) | |
| 81 | h = 60.0 * (g - b) / delta; | |
| 82 | else if (g == v) | |
| 83 | h = 120 + 60.0 * (b - r) / delta; | |
| 84 | else | |
| 85 | h = 240 + 60.0 * (r - g) / delta; | |
| 86 | ||
| 87 | if (h < 0.0) | |
| 88 | h += 360.0; | |
| 89 | if (h > 360.0) | |
| 90 | h -= 360.0; | |
| 91 | } | |
| 92 | ||
| 93 | *red = ROUND (h); | |
| 94 | *green = ROUND (s * 255.0); | |
| 95 | *blue = ROUND (v); | |
| 96 | } | |
| 97 | ||
| 98 | /** | |
| 99 | * hsv_to_rgb_int | |
| 100 | * @hue: The hue channel, returns the red channel | |
| 101 | * @saturation: The saturation channel, returns the green channel | |
| 102 | * @value: The value channel, returns the blue channel | |
| 103 | * | |
| 104 | * The arguments are pointers to int, with the values pointed to in the | |
| 105 | * following ranges: H [0, 360], S [0, 255], V [0, 255]. | |
| 106 | * | |
| 107 | * The function changes the arguments to point to the RGB value | |
| 108 | * corresponding, with the returned values all in the range [0, 255]. | |
| 109 | **/ | |
| 110 | inline void | |
| 111 | hsv_to_rgb_int (int *hue /* returns red */, | |
| 112 | int *saturation /* returns green */, | |
| 113 | int *value /* returns blue */) | |
| 114 | { | |
| 115 | double h, s, v, h_temp; | |
| 116 | double f, p, q, t; | |
| 117 | int i; | |
| 118 | ||
| 119 | if (*saturation == 0) | |
| 120 | { | |
| 121 | *hue = *value; | |
| 122 | *saturation = *value; | |
| 123 | // *value = *value; | |
| 124 | } | |
| 125 | else | |
| 126 | { | |
| 127 | h = *hue; | |
| 128 | s = *saturation / 255.0; | |
| 129 | v = *value / 255.0; | |
| 130 | ||
| 131 | if (h == 360) | |
| 132 | h_temp = 0; | |
| 133 | else | |
| 134 | h_temp = h; | |
| 135 | ||
| 136 | h_temp = h_temp / 60.0; | |
| 137 | i = (int) floor (h_temp); | |
| 138 | f = h_temp - i; | |
| 139 | p = v * (1.0 - s); | |
| 140 | q = v * (1.0 - (s * f)); | |
| 141 | t = v * (1.0 - (s * (1.0 - f))); | |
| 142 | ||
| 143 | switch (i) | |
| 144 | { | |
| 145 | case 0: | |
| 146 | *hue = ROUND (v * 255.0); | |
| 147 | *saturation = ROUND (t * 255.0); | |
| 148 | *value = ROUND (p * 255.0); | |
| 149 | break; | |
| 150 | ||
| 151 | case 1: | |
| 152 | *hue = ROUND (q * 255.0); | |
| 153 | *saturation = ROUND (v * 255.0); | |
| 154 | *value = ROUND (p * 255.0); | |
| 155 | break; | |
| 156 | ||
| 157 | case 2: | |
| 158 | *hue = ROUND (p * 255.0); | |
| 159 | *saturation = ROUND (v * 255.0); | |
| 160 | *value = ROUND (t * 255.0); | |
| 161 | break; | |
| 162 | ||
| 163 | case 3: | |
| 164 | *hue = ROUND (p * 255.0); | |
| 165 | *saturation = ROUND (q * 255.0); | |
| 166 | *value = ROUND (v * 255.0); | |
| 167 | break; | |
| 168 | ||
| 169 | case 4: | |
| 170 | *hue = ROUND (t * 255.0); | |
| 171 | *saturation = ROUND (p * 255.0); | |
| 172 | *value = ROUND (v * 255.0); | |
| 173 | break; | |
| 174 | ||
| 175 | case 5: | |
| 176 | *hue = ROUND (v * 255.0); | |
| 177 | *saturation = ROUND (p * 255.0); | |
| 178 | *value = ROUND (q * 255.0); | |
| 179 | break; | |
| 180 | } | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | /** | |
| 185 | * rgb_to_hsl_int | |
| 186 | * @red: Red channel, returns Hue channel | |
| 187 | * @green: Green channel, returns Lightness channel | |
| 188 | * @blue: Blue channel, returns Saturation channel | |
| 189 | * | |
| 190 | * The arguments are pointers to int representing channel values in the | |
| 191 | * RGB colorspace, and the values pointed to are all in the range [0, 255]. | |
| 192 | * | |
| 193 | * The function changes the arguments to point to the corresponding HLS | |
| 194 | * value with the values pointed to in the following ranges: H [0, 360], | |
| 195 | * L [0, 255], S [0, 255]. | |
| 196 | **/ | |
| 197 | inline void | |
| 198 | rgb_to_hsl_int (unsigned int *red /* returns red */, | |
| 199 | unsigned int *green /* returns green */, | |
| 200 | unsigned int *blue /* returns blue */) | |
| 201 | { | |
| 202 | unsigned int r, g, b; | |
| 203 | double h, s, l; | |
| 204 | unsigned int min, max; | |
| 205 | unsigned int delta; | |
| 206 | ||
| 207 | r = *red; | |
| 208 | g = *green; | |
| 209 | b = *blue; | |
| 210 | ||
| 211 | if (r > g) | |
| 212 | { | |
| 213 | max = MAX (r, b); | |
| 214 | min = MIN (g, b); | |
| 215 | } | |
| 216 | else | |
| 217 | { | |
| 218 | max = MAX (g, b); | |
| 219 | min = MIN (r, b); | |
| 220 | } | |
| 221 | ||
| 222 | l = (max + min) / 2.0; | |
| 223 | ||
| 224 | if (max == min) | |
| 225 | { | |
| 226 | s = 0.0; | |
| 227 | h = 0.0; | |
| 228 | } | |
| 229 | else | |
| 230 | { | |
| 231 | delta = (max - min); | |
| 232 | ||
| 233 | if (l < 128) | |
| 234 | s = 255 * (double) delta / (double) (max + min); | |
| 235 | else | |
| 236 | s = 255 * (double) delta / (double) (511 - max - min); | |
| 237 | ||
| 238 | if (r == max) | |
| 239 | h = (g - b) / (double) delta; | |
| 240 | else if (g == max) | |
| 241 | h = 2 + (b - r) / (double) delta; | |
| 242 | else | |
| 243 | h = 4 + (r - g) / (double) delta; | |
| 244 | ||
| 245 | h = h * 42.5; | |
| 246 | ||
| 247 | if (h < 0) | |
| 248 | h += 255; | |
| 249 | else if (h > 255) | |
| 250 | h -= 255; | |
| 251 | } | |
| 252 | ||
| 253 | *red = ROUND (h); | |
| 254 | *green = ROUND (s); | |
| 255 | *blue = ROUND (l); | |
| 256 | } | |
| 257 | ||
| 258 | inline int | |
| 259 | hsl_value_int (double n1, | |
| 260 | double n2, | |
| 261 | double hue) | |
| 262 | { | |
| 263 | double value; | |
| 264 | ||
| 265 | if (hue > 255) | |
| 266 | hue -= 255; | |
| 267 | else if (hue < 0) | |
| 268 | hue += 255; | |
| 269 | ||
| 270 | if (hue < 42.5) | |
| 271 | value = n1 + (n2 - n1) * (hue / 42.5); | |
| 272 | else if (hue < 127.5) | |
| 273 | value = n2; | |
| 274 | else if (hue < 170) | |
| 275 | value = n1 + (n2 - n1) * ((170 - hue) / 42.5); | |
| 276 | else | |
| 277 | value = n1; | |
| 278 | ||
| 279 | return ROUND (value * 255.0); | |
| 280 | } | |
| 281 | ||
| 282 | /** | |
| 283 | * hsl_to_rgb_int | |
| 284 | * @hue: Hue channel, returns Red channel | |
| 285 | * @saturation: Saturation channel, returns Green channel | |
| 286 | * @lightness: Lightness channel, returns Blue channel | |
| 287 | * | |
| 288 | * The arguments are pointers to int, with the values pointed to in the | |
| 289 | * following ranges: H [0, 360], L [0, 255], S [0, 255]. | |
| 290 | * | |
| 291 | * The function changes the arguments to point to the RGB value | |
| 292 | * corresponding, with the returned values all in the range [0, 255]. | |
| 293 | **/ | |
| 294 | inline void | |
| 295 | hsl_to_rgb_int (unsigned int *hue /* returns red */, | |
| 296 | unsigned int *saturation /* returns green */, | |
| 297 | unsigned int *lightness /* returns blue */) | |
| 298 | { | |
| 299 | double h, s, l; | |
| 300 | ||
| 301 | h = *hue; | |
| 302 | s = *saturation; | |
| 303 | l = *lightness; | |
| 304 | ||
| 305 | if (s == 0) | |
| 306 | { | |
| 307 | /* achromatic case */ | |
| 308 | *hue = (int)l; | |
| 309 | *lightness = (int)l; | |
| 310 | *saturation = (int)l; | |
| 311 | } | |
| 312 | else | |
| 313 | { | |
| 314 | double m1, m2; | |
| 315 | ||
| 316 | if (l < 128) | |
| 317 | m2 = (l * (255 + s)) / 65025.0; | |
| 318 | else | |
| 319 | m2 = (l + s - (l * s) / 255.0) / 255.0; | |
| 320 | ||
| 321 | m1 = (l / 127.5) - m2; | |
| 322 | ||
| 323 | /* chromatic case */ | |
| 324 | *hue = hsl_value_int (m1, m2, h + 85); | |
| 325 | *saturation = hsl_value_int (m1, m2, h); | |
| 326 | *lightness = hsl_value_int (m1, m2, h - 85); | |
| 327 | } | |
| 328 | } | |
| 329 | ||
| 330 | /** | |
| 331 | * gimp_rgb_to_cmyk_int: | |
| 332 | * @red: the red channel; returns the cyan value (0-255) | |
| 333 | * @green: the green channel; returns the magenta value (0-255) | |
| 334 | * @blue: the blue channel; returns the yellow value (0-255) | |
| 335 | * @pullout: the percentage of black to pull out (0-100); returns | |
| 336 | * the black value (0-255) | |
| 337 | * | |
| 338 | * Does a naive conversion from RGB to CMYK colorspace. A simple | |
| 339 | * formula that doesn't take any color-profiles into account is used. | |
| 340 | * The amount of black pullout how can be controlled via the @pullout | |
| 341 | * parameter. A @pullout value of 0 makes this a conversion to CMY. | |
| 342 | * A value of 100 causes the maximum amount of black to be pulled out. | |
| 343 | **/ | |
| 344 | inline void | |
| 345 | gimp_rgb_to_cmyk_int (int *red, | |
| 346 | int *green, | |
| 347 | int *blue, | |
| 348 | int *pullout) | |
| 349 | { | |
| 350 | int c, m, y; | |
| 351 | ||
| 352 | c = 255 - *red; | |
| 353 | m = 255 - *green; | |
| 354 | y = 255 - *blue; | |
| 355 | ||
| 356 | if (*pullout == 0) | |
| 357 | { | |
| 358 | *red = c; | |
| 359 | *green = m; | |
| 360 | *blue = y; | |
| 361 | } | |
| 362 | else | |
| 363 | { | |
| 364 | int k = 255; | |
| 365 | ||
| 366 | if (c < k) k = c; | |
| 367 | if (m < k) k = m; | |
| 368 | if (y < k) k = y; | |
| 369 | ||
| 370 | k = (k * CLAMP (*pullout, 0, 100)) / 100; | |
| 371 | ||
| 372 | *red = ((c - k) << 8) / (256 - k); | |
| 373 | *green = ((m - k) << 8) / (256 - k); | |
| 374 | *blue = ((y - k) << 8) / (256 - k); | |
| 375 | *pullout = k; | |
| 376 | } | |
| 377 | } | |
| 378 | ||
| 379 | /** | |
| 380 | * gimp_cmyk_to_rgb_int: | |
| 381 | * @cyan: the cyan channel; returns the red value (0-255) | |
| 382 | * @magenta: the magenta channel; returns the green value (0-255) | |
| 383 | * @yellow: the yellow channel; returns the blue value (0-255) | |
| 384 | * @black: the black channel (0-255); doesn't change | |
| 385 | * | |
| 386 | * Does a naive conversion from CMYK to RGB colorspace. A simple | |
| 387 | * formula that doesn't take any color-profiles into account is used. | |
| 388 | **/ | |
| 389 | inline void | |
| 390 | cmyk_to_rgb_int (int *cyan, | |
| 391 | int *magenta, | |
| 392 | int *yellow, | |
| 393 | int *black) | |
| 394 | { | |
| 395 | int c, m, y, k; | |
| 396 | ||
| 397 | c = *cyan; | |
| 398 | m = *magenta; | |
| 399 | y = *yellow; | |
| 400 | k = *black; | |
| 401 | ||
| 402 | if (k) | |
| 403 | { | |
| 404 | c = ((c * (256 - k)) >> 8) + k; | |
| 405 | m = ((m * (256 - k)) >> 8) + k; | |
| 406 | y = ((y * (256 - k)) >> 8) + k; | |
| 407 | } | |
| 408 | ||
| 409 | *cyan = 255 - c; | |
| 410 | *magenta = 255 - m; | |
| 411 | *yellow = 255 - y; | |
| 412 | } | |
| 413 | ||
| 414 | ||
| 415 | #endif |
| 0 | #ifndef INCLUDED_FREI0R_MATH_H | |
| 1 | #define INCLUDED_FREI0R_MATH_H | |
| 2 | ||
| 3 | /* | |
| 4 | ||
| 5 | Code stripped from The Gimp: | |
| 6 | INT_MULT(a,b,t) | |
| 7 | INT_MULT3(a,b,c,t) | |
| 8 | INT_BLEND(a,b,alpha,tmp) | |
| 9 | CLAMP | |
| 10 | ROUND | |
| 11 | MAX255 | |
| 12 | ||
| 13 | Code stripped from Drone: | |
| 14 | CLAMP0255 | |
| 15 | SQR | |
| 16 | */ | |
| 17 | ||
| 18 | /* Clamps a int32-range int between 0 and 255 inclusive. */ | |
| 19 | #ifndef CLAMP0255 | |
| 20 | static inline unsigned char CLAMP0255(int32_t a) | |
| 21 | { | |
| 22 | return (unsigned char) | |
| 23 | ( (((-a) >> 31) & a) // 0 if the number was negative | |
| 24 | | (255 - a) >> 31); // -1 if the number was greater than 255 | |
| 25 | } | |
| 26 | #endif | |
| 27 | ||
| 28 | /* Provided temporary int t, returns a * b / 255 */ | |
| 29 | #ifndef INT_MULT | |
| 30 | #define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8)) | |
| 31 | #endif | |
| 32 | ||
| 33 | /* This version of INT_MULT3 is very fast, but suffers from some | |
| 34 | slight roundoff errors. It returns the correct result 99.987 | |
| 35 | percent of the time */ | |
| 36 | #ifndef INT_MULT3 | |
| 37 | #define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c) + 0x7F5B, \ | |
| 38 | ((((t) >> 7) + (t)) >> 16)) | |
| 39 | #endif | |
| 40 | ||
| 41 | #ifndef INT_BLEND | |
| 42 | #define INT_BLEND(a,b,alpha,tmp) (INT_MULT((a) - (b), alpha, tmp) + (b)) | |
| 43 | #endif | |
| 44 | ||
| 45 | #ifndef CLAMP | |
| 46 | //! Clamp x at min and max | |
| 47 | #define CLAMP(x,min,max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) | |
| 48 | #endif | |
| 49 | ||
| 50 | #ifndef ROUND | |
| 51 | //! Round. | |
| 52 | #define ROUND(x) ((int32_t)((x)+0.5)) | |
| 53 | #endif | |
| 54 | ||
| 55 | #ifndef SQR | |
| 56 | //! Square. | |
| 57 | #define SQR(x) ((x) * (x)) | |
| 58 | #endif | |
| 59 | ||
| 60 | #ifndef MAX255 | |
| 61 | //! Limit a (0->511) int to 255. | |
| 62 | uint8_t MAX255(uint32_t a) { return (uint8_t) (a | ((a & 256) - ((a & 256) >> 8))); } | |
| 63 | #endif | |
| 64 | ||
| 65 | #ifndef MIN | |
| 66 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) | |
| 67 | #endif | |
| 68 | ||
| 69 | #ifndef MAX | |
| 70 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) | |
| 71 | #endif | |
| 72 | ||
| 73 | #endif |
| 11 | 11 | if (${Cairo_FOUND}) |
| 12 | 12 | add_subdirectory (cairoimagegrid) |
| 13 | 13 | add_subdirectory (cairogradient) |
| 14 | add_subdirectory (mirr0r) | |
| 14 | 15 | endif (${Cairo_FOUND}) |
| 15 | 16 | |
| 16 | 17 | add_subdirectory (3dflippo) |
| 68 | 69 | add_subdirectory (nervous) |
| 69 | 70 | add_subdirectory (normaliz0r) |
| 70 | 71 | add_subdirectory (nosync0r) |
| 72 | add_subdirectory (ntsc) | |
| 71 | 73 | add_subdirectory (perspective) |
| 72 | 74 | add_subdirectory (pixeliz0r) |
| 73 | 75 | add_subdirectory (pixs0r) |
| 21 | 21 | * |
| 22 | 22 | */ |
| 23 | 23 | #include "frei0r.hpp" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | #include <string.h> |
| 27 | 27 | #include <climits> |
| 28 | 28 | |
| 29 | 29 | //#include <stdio.h> |
| 30 | 30 | #include <frei0r.h> |
| 31 | #include <frei0r_math.h> | |
| 31 | #include <frei0r/math.h> | |
| 32 | 32 | #include <stdlib.h> |
| 33 | 33 | #include <math.h> |
| 34 | 34 | #include <assert.h> |
| 27 | 27 | #include <stdio.h> |
| 28 | 28 | |
| 29 | 29 | #include "frei0r.h" |
| 30 | #include "frei0r_math.h" | |
| 30 | #include "frei0r/math.h" | |
| 31 | 31 | |
| 32 | 32 | static const float bbWB[][3] = |
| 33 | 33 | { |
| 68 | 68 | |
| 69 | 69 | #include <sys/types.h> |
| 70 | 70 | #include <string.h> |
| 71 | #include "frei0r_math.h" | |
| 71 | #include "frei0r/math.h" | |
| 72 | 72 | |
| 73 | 73 | //--------------------------------------------------------- |
| 74 | 74 | //koeficienti za biquad lowpass iz f in q |
| 20 | 20 | #include <assert.h> |
| 21 | 21 | |
| 22 | 22 | #include "frei0r.h" |
| 23 | #include "frei0r_math.h" | |
| 23 | #include "frei0r/math.h" | |
| 24 | 24 | |
| 25 | 25 | typedef struct brightness_instance |
| 26 | 26 | { |
| 27 | 27 | #include <frei0r.h> |
| 28 | 28 | #include <string.h> |
| 29 | 29 | #include <math.h> |
| 30 | #include "frei0r_math.h" | |
| 30 | #include "frei0r/math.h" | |
| 31 | 31 | #include "interp.h" |
| 32 | 32 | |
| 33 | 33 | //---------------------------------------- |
| 26 | 26 | #include <math.h> |
| 27 | 27 | |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_cairo.h" | |
| 29 | #include "frei0r/cairo.h" | |
| 30 | 30 | |
| 31 | 31 | typedef struct cairo_gradient_instance |
| 32 | 32 | { |
| 23 | 23 | #include <cairo.h> |
| 24 | 24 | |
| 25 | 25 | #include "frei0r.h" |
| 26 | #include "frei0r_cairo.h" | |
| 26 | #include "frei0r/cairo.h" | |
| 27 | 27 | |
| 28 | 28 | #define MAX_ROWS 20 |
| 29 | 29 | #define MAX_COLUMNS 20 |
| 23 | 23 | #include <stdio.h> |
| 24 | 24 | |
| 25 | 25 | #include "frei0r.h" |
| 26 | #include "frei0r_math.h" | |
| 26 | #include "frei0r/math.h" | |
| 27 | 27 | |
| 28 | 28 | #define MAXNUM 40 |
| 29 | 29 |
| 87 | 87 | #endif |
| 88 | 88 | |
| 89 | 89 | #include "frei0r.h" |
| 90 | #include "frei0r_math.h" | |
| 90 | #include "frei0r/math.h" | |
| 91 | 91 | |
| 92 | 92 | enum ParamIndex { |
| 93 | 93 | NEUTRAL_COLOR, |
| 22 | 22 | #include <stdio.h> |
| 23 | 23 | |
| 24 | 24 | #include "frei0r.h" |
| 25 | #include "frei0r_math.h" | |
| 25 | #include "frei0r/math.h" | |
| 26 | 26 | |
| 27 | 27 | typedef struct colordistance_instance |
| 28 | 28 | { |
| 26 | 26 | #include <stdio.h> |
| 27 | 27 | #include <math.h> |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_math.h" | |
| 29 | #include "frei0r/math.h" | |
| 30 | 30 | |
| 31 | 31 | double PI=3.14159265358979; |
| 32 | 32 |
| 31 | 31 | #include <assert.h> |
| 32 | 32 | |
| 33 | 33 | #include "frei0r.h" |
| 34 | #include "frei0r_math.h" | |
| 34 | #include "frei0r/math.h" | |
| 35 | 35 | |
| 36 | 36 | #define GIMP_RGB_LUMINANCE_RED (0.2126) |
| 37 | 37 | #define GIMP_RGB_LUMINANCE_GREEN (0.7152) |
| 21 | 21 | #include <stdio.h> |
| 22 | 22 | |
| 23 | 23 | #include "frei0r.h" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | typedef struct contrast0r_instance |
| 27 | 27 | { |
| 27 | 27 | #include <stdio.h> |
| 28 | 28 | |
| 29 | 29 | #include "frei0r.h" |
| 30 | #include "frei0r_math.h" | |
| 30 | #include "frei0r/math.h" | |
| 31 | 31 | |
| 32 | 32 | #define MAX3(a, b, c) ( ( a > b && a > c) ? a : (b > c ? b : c) ) |
| 33 | 33 | #define MIN3(a, b, c) ( ( a < b && a < c) ? a : (b < c ? b : c) ) |
| 26 | 26 | #include <math.h> |
| 27 | 27 | |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_math.h" | |
| 29 | #include "frei0r/math.h" | |
| 30 | 30 | |
| 31 | 31 | int ditherMagic2x2Matrix[] = { |
| 32 | 32 | 0, 2, |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | #include <stdlib.h> |
| 22 | 22 | |
| 23 | 23 | class edgeglow : public frei0r::filter |
| 26 | 26 | #include <math.h> |
| 27 | 27 | |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_math.h" | |
| 29 | #include "frei0r/math.h" | |
| 30 | 30 | |
| 31 | 31 | double PI = 3.14159; |
| 32 | 32 | double pixelScale = 255.9; |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | #include <string.h> |
| 22 | 22 | |
| 23 | 23 | #include <cstring> |
| 19 | 19 | #include <string.h> |
| 20 | 20 | #include <opencv2/opencv.hpp> |
| 21 | 21 | #include "frei0r.hpp" |
| 22 | #include "frei0r_math.h" | |
| 22 | #include "frei0r/math.h" | |
| 23 | 23 | |
| 24 | 24 | class TrackedObj { |
| 25 | 25 | public: |
| 21 | 21 | #include <string.h> |
| 22 | 22 | #include <opencv2/opencv.hpp> |
| 23 | 23 | #include "frei0r.hpp" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | #define USE_ROI |
| 27 | 27 | #define PAD (40) |
| 0 | 0 | #include <stdlib.h> |
| 1 | 1 | #include "frei0r.h" |
| 2 | #include "frei0r_math.h" | |
| 2 | #include "frei0r/math.h" | |
| 3 | 3 | |
| 4 | 4 | |
| 5 | 5 | typedef struct flimgrain_instance |
| 21 | 21 | #include <assert.h> |
| 22 | 22 | |
| 23 | 23 | #include "frei0r.h" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | #define MAX_GAMMA 4.0 |
| 27 | 27 |
| 0 | 0 | #include <stdlib.h> |
| 1 | 1 | #include <math.h> |
| 2 | 2 | #include "frei0r.h" |
| 3 | #include "frei0r_math.h" | |
| 3 | #include "frei0r/math.h" | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | // let's try to walk through everything because i'm a moron |
| 24 | 24 | #include <time.h> |
| 25 | 25 | |
| 26 | 26 | #include "frei0r.h" |
| 27 | #include "frei0r_math.h" | |
| 27 | #include "frei0r/math.h" | |
| 28 | 28 | |
| 29 | 29 | /* cheap & fast randomizer (by Fukuchi Kentarou) */ |
| 30 | 30 | static uint32_t randval; |
| 19 | 19 | #include <math.h> |
| 20 | 20 | #include "frei0r.h" |
| 21 | 21 | #include <stdlib.h> |
| 22 | #include "blur.h" | |
| 22 | #include "frei0r/blur.h" | |
| 23 | 23 | |
| 24 | 24 | typedef struct glow_instance { |
| 25 | 25 | double blur; |
| 6 | 6 | * |
| 7 | 7 | * Paul Haeberli - 1993 |
| 8 | 8 | */ |
| 9 | #include "frei0r_math.h" | |
| 9 | #include "frei0r/math.h" | |
| 10 | 10 | #include <math.h> |
| 11 | 11 | #include <stdio.h> |
| 12 | 12 |
| 21 | 21 | #include <assert.h> |
| 22 | 22 | |
| 23 | 23 | #include "frei0r.h" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | typedef struct lenscorrection_instance |
| 27 | 27 | { |
| 0 | 0 | # Set C99 flag for gcc |
| 1 | 1 | if (CMAKE_COMPILER_IS_GNUCC) |
| 2 | set(CMAKE_C_FLAGS "-std=c99") | |
| 2 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") | |
| 3 | 3 | endif (CMAKE_COMPILER_IS_GNUCC) |
| 4 | 4 | |
| 5 | 5 | set (SOURCES levels.c) |
| 23 | 23 | #include <stdio.h> |
| 24 | 24 | |
| 25 | 25 | #include "frei0r.h" |
| 26 | #include "frei0r_math.h" | |
| 26 | #include "frei0r/math.h" | |
| 27 | 27 | |
| 28 | 28 | enum ChannelChoice |
| 29 | 29 | { |
| 21 | 21 | #include <assert.h> |
| 22 | 22 | |
| 23 | 23 | #include "frei0r.h" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | #define MAX_SATURATION 8.0 |
| 27 | 27 |
| 19 | 19 | #include <math.h> |
| 20 | 20 | #include "frei0r.h" |
| 21 | 21 | #include <stdlib.h> |
| 22 | #include "blur.h" | |
| 22 | #include "frei0r/blur.h" | |
| 23 | 23 | |
| 24 | 24 | typedef struct mask0mate_instance { |
| 25 | 25 | double left, top, right, bottom; |
| 0 | set (SOURCES mirr0r.cpp) | |
| 1 | set (TARGET mirr0r) | |
| 2 | ||
| 3 | include_directories(${Cairo_INCLUDE_DIR}) | |
| 4 | set(LIBS ${LIBS} ${Cairo_LIBRARY}) | |
| 5 | ||
| 6 | if (MSVC) | |
| 7 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 8 | endif (MSVC) | |
| 9 | ||
| 10 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 11 | ||
| 12 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 13 | target_link_libraries(mirr0r ${LIBS}) | |
| 14 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) |
| 0 | /* | |
| 1 | * Copyright (C) 2024 Johann JEG (johannjeg1057@gmail.com) | |
| 2 | * | |
| 3 | * This file is a Frei0r plugin. | |
| 4 | * | |
| 5 | * This program is free software: you can redistribute it and/or | |
| 6 | * modify it under the terms of the GNU General Public License | |
| 7 | * as published by the Free Software Foundation, either version 3 of | |
| 8 | * the License, or (at your option) any later version. | |
| 9 | * | |
| 10 | * This program is distributed in the hope that it will be useful, | |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | * GNU General Public License for more details. | |
| 14 | * | |
| 15 | * You should have received a copy of the GNU General Public License | |
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 17 | */ | |
| 18 | ||
| 19 | #include "frei0r.hpp" | |
| 20 | #include <cairo.h> | |
| 21 | #define _USE_MATH_DEFINES | |
| 22 | #include <cmath> | |
| 23 | ||
| 24 | class Mirr0r : public frei0r::filter { | |
| 25 | ||
| 26 | private: | |
| 27 | ||
| 28 | unsigned int width; | |
| 29 | unsigned int height; | |
| 30 | double x_offset; | |
| 31 | double y_offset; | |
| 32 | double zoom; | |
| 33 | double rotation; | |
| 34 | ||
| 35 | public: | |
| 36 | ||
| 37 | Mirr0r(unsigned int width, unsigned int height){ | |
| 38 | register_param(this->x_offset, "x_offset", "Horizontal offset for image positioning."); | |
| 39 | register_param(this->y_offset, "y_offset", "Vertical offset for image positioning."); | |
| 40 | register_param(this->zoom, "zoom", "Zoom level for image scaling."); | |
| 41 | register_param(this->rotation, "rotation", "Rotation angle in degrees."); | |
| 42 | ||
| 43 | this->width = width; | |
| 44 | this->height = height; | |
| 45 | this->x_offset = 0.0; | |
| 46 | this->y_offset = 0.0; | |
| 47 | this->zoom = 0.5; | |
| 48 | this->rotation = 0.0; | |
| 49 | } | |
| 50 | ||
| 51 | ~Mirr0r(){ | |
| 52 | ||
| 53 | } | |
| 54 | ||
| 55 | void clearScreen(cairo_t* cr, int width, int height) { | |
| 56 | cairo_save (cr); | |
| 57 | cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); | |
| 58 | cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); | |
| 59 | cairo_paint (cr); | |
| 60 | cairo_restore (cr); | |
| 61 | } | |
| 62 | ||
| 63 | virtual void update(double time, uint32_t* out, const uint32_t* in) { | |
| 64 | ||
| 65 | int w = this->width; | |
| 66 | int h = this->height; | |
| 67 | ||
| 68 | // Calculate the stride for the image surface based on width and format | |
| 69 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); | |
| 70 | ||
| 71 | // Create a Cairo surface for the destination image | |
| 72 | cairo_surface_t* dest_image = cairo_image_surface_create_for_data((unsigned char*)out, | |
| 73 | CAIRO_FORMAT_ARGB32, | |
| 74 | w, | |
| 75 | h, | |
| 76 | stride); | |
| 77 | ||
| 78 | // Create a Cairo drawing context for the destination surface | |
| 79 | cairo_t *cr = cairo_create(dest_image); | |
| 80 | ||
| 81 | // Clear the screen | |
| 82 | clearScreen(cr, w, h); | |
| 83 | ||
| 84 | // Create a Cairo surface for the source image | |
| 85 | cairo_surface_t *image = cairo_image_surface_create_for_data((unsigned char*)in, | |
| 86 | CAIRO_FORMAT_ARGB32, | |
| 87 | w, | |
| 88 | h, | |
| 89 | stride); | |
| 90 | // Create a pattern from the source image surface | |
| 91 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); | |
| 92 | // Set the pattern extend mode to reflect (mirror) when the pattern is out of bounds | |
| 93 | cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); | |
| 94 | ||
| 95 | // Initialize the transformation matrix | |
| 96 | cairo_matrix_t matrix; | |
| 97 | cairo_matrix_init_identity(&matrix); | |
| 98 | ||
| 99 | // Calculate the center coordinates of the destination image | |
| 100 | float center_x = (float)w / 2.0f; | |
| 101 | float center_y = (float)h / 2.0f; | |
| 102 | ||
| 103 | float scale_factor = 1.5f - this->zoom; | |
| 104 | ||
| 105 | // Apply transformations | |
| 106 | cairo_matrix_translate(&matrix, center_x - (this->x_offset) * w, center_y - (this->y_offset) * h); | |
| 107 | cairo_matrix_scale(&matrix, scale_factor, scale_factor); | |
| 108 | cairo_matrix_rotate(&matrix, this->rotation * M_PI); | |
| 109 | cairo_matrix_translate(&matrix, -center_x, -center_y); | |
| 110 | ||
| 111 | // Set the transformation matrix for the pattern | |
| 112 | cairo_pattern_set_matrix(pattern, &matrix); | |
| 113 | // Set the source pattern to be used for drawing | |
| 114 | cairo_set_source(cr, pattern); | |
| 115 | ||
| 116 | // Draw a rectangle covering the entire image area | |
| 117 | cairo_rectangle(cr, 0, 0, w, h); | |
| 118 | // Fill the rectangle with the source pattern | |
| 119 | cairo_fill(cr); | |
| 120 | ||
| 121 | // Clean up resources | |
| 122 | cairo_pattern_destroy (pattern); | |
| 123 | cairo_surface_destroy (image); | |
| 124 | cairo_surface_destroy (dest_image); | |
| 125 | cairo_destroy (cr); | |
| 126 | } | |
| 127 | }; | |
| 128 | ||
| 129 | frei0r::construct<Mirr0r> plugin( | |
| 130 | "Mirr0r", | |
| 131 | "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.", | |
| 132 | "Johann JEG", | |
| 133 | 1, 0, | |
| 134 | F0R_COLOR_MODEL_RGBA8888); | |
| 135 | ⏎ |
| 18 | 18 | #include <cairo.h> |
| 19 | 19 | #endif |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | #include "gradientlut.hpp" |
| 23 | 23 | #include <string> |
| 24 | 24 | #include <stdlib.h> |
| 73 | 73 | #include <stdio.h> |
| 74 | 74 | |
| 75 | 75 | #include "frei0r.h" |
| 76 | #include "frei0r_math.h" | |
| 76 | #include "frei0r/math.h" | |
| 77 | 77 | |
| 78 | 78 | #define MAX_HISTORY_LEN 128 |
| 79 | 79 |
| 9 | 9 | { |
| 10 | 10 | hsync = 0.0; |
| 11 | 11 | register_param(hsync,"HSync","the hsync offset"); |
| 12 | vsync = 0.0; | |
| 13 | register_param(vsync,"VSync","the vsync offset"); | |
| 12 | 14 | } |
| 13 | 15 | |
| 14 | 16 | virtual void update(double time, |
| 15 | 17 | uint32_t* out, |
| 16 | 18 | const uint32_t* in) |
| 17 | 19 | { |
| 18 | unsigned int | |
| 19 | first_line=static_cast<unsigned int>(height*std::fmod(std::fabs(hsync),1.0)); | |
| 20 | if (hsync == 0.0 && vsync == 0.0) { | |
| 21 | std::copy(in, in+width*height, out); | |
| 22 | } else if (vsync == 0.0) { | |
| 23 | unsigned int | |
| 24 | first_line=static_cast<unsigned int>(height*std::fmod(std::fabs(hsync),1.0)); | |
| 20 | 25 | |
| 21 | std::copy(in+width*first_line, in+width*height, out); | |
| 22 | std::copy(in, in+width*first_line, out+width*(height-first_line)); | |
| 26 | std::copy(in+width*first_line, in+width*height, out); | |
| 27 | std::copy(in, in+width*first_line, out+width*(height-first_line)); | |
| 28 | } else { | |
| 29 | unsigned int hoffset=static_cast<unsigned int>(height*std::fmod(std::fabs(hsync),1.0)); | |
| 30 | unsigned int voffset=static_cast<unsigned int>(width*std::fmod(std::fabs(vsync),1.0)); | |
| 31 | for (unsigned int src_line = 0; src_line < height; src_line++) { | |
| 32 | unsigned int dst_line = (src_line + hoffset) % height; | |
| 33 | const uint32_t* src_pix = in + (width * src_line); | |
| 34 | uint32_t* dst_pix = out + (width * dst_line); | |
| 35 | std::copy(src_pix, src_pix + width - voffset, dst_pix + voffset); | |
| 36 | std::copy(src_pix + width - voffset, src_pix + width, dst_pix); | |
| 37 | } | |
| 38 | } | |
| 23 | 39 | } |
| 24 | 40 | |
| 25 | 41 | private: |
| 26 | 42 | double hsync; |
| 43 | double vsync; | |
| 27 | 44 | }; |
| 28 | 45 | |
| 29 | 46 |
| 0 | set (SOURCES crt_core.c crt_ntsc.c ntsc-effect.c) | |
| 1 | set (TARGET ntsc) | |
| 2 | ||
| 3 | if (MSVC) | |
| 4 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 5 | endif (MSVC) | |
| 6 | ||
| 7 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 8 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 9 | ||
| 10 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR})⏎ |
| 0 | /*****************************************************************************/ | |
| 1 | /* | |
| 2 | * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation | |
| 3 | * | |
| 4 | * by EMMIR 2018-2023 | |
| 5 | * | |
| 6 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos | |
| 7 | * Discord: https://discord.com/invite/hdYctSmyQJ | |
| 8 | */ | |
| 9 | /*****************************************************************************/ | |
| 10 | #include "crt_core.h" | |
| 11 | ||
| 12 | #include <stdlib.h> | |
| 13 | #include <string.h> | |
| 14 | ||
| 15 | /* ensure negative values for x get properly modulo'd */ | |
| 16 | #define POSMOD(x, n) (((x) % (n) + (n)) % (n)) | |
| 17 | ||
| 18 | static int sigpsin15[18] = { /* significant points on sine wave (15-bit) */ | |
| 19 | 0x0000, | |
| 20 | 0x0c88,0x18f8,0x2528,0x30f8,0x3c50,0x4718,0x5130,0x5a80, | |
| 21 | 0x62f0,0x6a68,0x70e0,0x7640,0x7a78,0x7d88,0x7f60,0x8000, | |
| 22 | 0x7f60 | |
| 23 | }; | |
| 24 | ||
| 25 | static int | |
| 26 | sintabil8(int n) | |
| 27 | { | |
| 28 | int f, i, a, b; | |
| 29 | ||
| 30 | /* looks scary but if you don't change T14_2PI | |
| 31 | * it won't cause out of bounds memory reads | |
| 32 | */ | |
| 33 | f = n >> 0 & 0xff; | |
| 34 | i = n >> 8 & 0xff; | |
| 35 | a = sigpsin15[i]; | |
| 36 | b = sigpsin15[i + 1]; | |
| 37 | return (a + ((b - a) * f >> 8)); | |
| 38 | } | |
| 39 | ||
| 40 | /* 14-bit interpolated sine/cosine */ | |
| 41 | extern void | |
| 42 | crt_sincos14(int *s, int *c, int n) | |
| 43 | { | |
| 44 | int h; | |
| 45 | ||
| 46 | n &= T14_MASK; | |
| 47 | h = n & ((T14_2PI >> 1) - 1); | |
| 48 | ||
| 49 | if (h > ((T14_2PI >> 2) - 1)) { | |
| 50 | *c = -sintabil8(h - (T14_2PI >> 2)); | |
| 51 | *s = sintabil8((T14_2PI >> 1) - h); | |
| 52 | } else { | |
| 53 | *c = sintabil8((T14_2PI >> 2) - h); | |
| 54 | *s = sintabil8(h); | |
| 55 | } | |
| 56 | if (n > ((T14_2PI >> 1) - 1)) { | |
| 57 | *c = -*c; | |
| 58 | *s = -*s; | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | /*****************************************************************************/ | |
| 63 | /********************************* FILTERS ***********************************/ | |
| 64 | /*****************************************************************************/ | |
| 65 | ||
| 66 | /* convolution is much faster but the EQ looks softer, more authentic, and more analog */ | |
| 67 | #define USE_CONVOLUTION 0 | |
| 68 | #define USE_7_SAMPLE_KERNEL 1 | |
| 69 | #define USE_6_SAMPLE_KERNEL 0 | |
| 70 | #define USE_5_SAMPLE_KERNEL 0 | |
| 71 | ||
| 72 | #if (CRT_CC_SAMPLES != 4) | |
| 73 | /* the current convolutions do not filter properly at > 4 samples */ | |
| 74 | #undef USE_CONVOLUTION | |
| 75 | #define USE_CONVOLUTION 0 | |
| 76 | #endif | |
| 77 | ||
| 78 | #if USE_CONVOLUTION | |
| 79 | ||
| 80 | /* NOT 3 band equalizer, faster convolution instead. | |
| 81 | * eq function names preserved to keep code clean | |
| 82 | */ | |
| 83 | static struct EQF { | |
| 84 | int h[7]; | |
| 85 | } eqY, eqI, eqQ; | |
| 86 | ||
| 87 | /* params unused to keep the function the same */ | |
| 88 | static void | |
| 89 | init_eq(struct EQF *f, | |
| 90 | int f_lo, int f_hi, int rate, | |
| 91 | int g_lo, int g_mid, int g_hi) | |
| 92 | { | |
| 93 | memset(f, 0, sizeof(struct EQF)); | |
| 94 | } | |
| 95 | ||
| 96 | static void | |
| 97 | reset_eq(struct EQF *f) | |
| 98 | { | |
| 99 | memset(f->h, 0, sizeof(f->h)); | |
| 100 | } | |
| 101 | ||
| 102 | static int | |
| 103 | eqf(struct EQF *f, int s) | |
| 104 | { | |
| 105 | int i; | |
| 106 | int *h = f->h; | |
| 107 | ||
| 108 | for (i = 6; i > 0; i--) { | |
| 109 | h[i] = h[i - 1]; | |
| 110 | } | |
| 111 | h[0] = s; | |
| 112 | #if USE_7_SAMPLE_KERNEL | |
| 113 | /* index : 0 1 2 3 4 5 6 */ | |
| 114 | /* weight: 1 4 7 8 7 4 1 */ | |
| 115 | return (s + h[6] + ((h[1] + h[5]) * 4) + ((h[2] + h[4]) * 7) + (h[3] * 8)) >> 5; | |
| 116 | #elif USE_6_SAMPLE_KERNEL | |
| 117 | /* index : 0 1 2 3 4 5 */ | |
| 118 | /* weight: 1 3 4 4 3 1 */ | |
| 119 | return (s + h[5] + 3 * (h[1] + h[4]) + 4 * (h[2] + h[3])) >> 4; | |
| 120 | #elif USE_5_SAMPLE_KERNEL | |
| 121 | /* index : 0 1 2 3 4 */ | |
| 122 | /* weight: 1 2 2 2 1 */ | |
| 123 | return (s + h[4] + ((h[1] + h[2] + h[3]) << 1)) >> 3; | |
| 124 | #else | |
| 125 | /* index : 0 1 2 3 */ | |
| 126 | /* weight: 1 1 1 1*/ | |
| 127 | return (s + h[3] + h[1] + h[2]) >> 2; | |
| 128 | #endif | |
| 129 | } | |
| 130 | ||
| 131 | #else | |
| 132 | ||
| 133 | #define HISTLEN 3 | |
| 134 | #define HISTOLD (HISTLEN - 1) /* oldest entry */ | |
| 135 | #define HISTNEW 0 /* newest entry */ | |
| 136 | ||
| 137 | #define EQ_P 16 /* if changed, the gains will need to be adjusted */ | |
| 138 | #define EQ_R (1 << (EQ_P - 1)) /* rounding */ | |
| 139 | /* three band equalizer */ | |
| 140 | static struct EQF { | |
| 141 | int lf, hf; /* fractions */ | |
| 142 | int g[3]; /* gains */ | |
| 143 | int fL[4]; | |
| 144 | int fH[4]; | |
| 145 | int h[HISTLEN]; /* history */ | |
| 146 | } eqY, eqI, eqQ; | |
| 147 | ||
| 148 | /* f_lo - low cutoff frequency | |
| 149 | * f_hi - high cutoff frequency | |
| 150 | * rate - sampling rate | |
| 151 | * g_lo, g_mid, g_hi - gains | |
| 152 | */ | |
| 153 | static void | |
| 154 | init_eq(struct EQF *f, | |
| 155 | int f_lo, int f_hi, int rate, | |
| 156 | int g_lo, int g_mid, int g_hi) | |
| 157 | { | |
| 158 | int sn, cs; | |
| 159 | ||
| 160 | memset(f, 0, sizeof(struct EQF)); | |
| 161 | ||
| 162 | f->g[0] = g_lo; | |
| 163 | f->g[1] = g_mid; | |
| 164 | f->g[2] = g_hi; | |
| 165 | ||
| 166 | crt_sincos14(&sn, &cs, T14_PI * f_lo / rate); | |
| 167 | #if (EQ_P >= 15) | |
| 168 | f->lf = 2 * (sn << (EQ_P - 15)); | |
| 169 | #else | |
| 170 | f->lf = 2 * (sn >> (15 - EQ_P)); | |
| 171 | #endif | |
| 172 | crt_sincos14(&sn, &cs, T14_PI * f_hi / rate); | |
| 173 | #if (EQ_P >= 15) | |
| 174 | f->hf = 2 * (sn << (EQ_P - 15)); | |
| 175 | #else | |
| 176 | f->hf = 2 * (sn >> (15 - EQ_P)); | |
| 177 | #endif | |
| 178 | } | |
| 179 | ||
| 180 | static void | |
| 181 | reset_eq(struct EQF *f) | |
| 182 | { | |
| 183 | memset(f->fL, 0, sizeof(f->fL)); | |
| 184 | memset(f->fH, 0, sizeof(f->fH)); | |
| 185 | memset(f->h, 0, sizeof(f->h)); | |
| 186 | } | |
| 187 | ||
| 188 | static int | |
| 189 | eqf(struct EQF *f, int s) | |
| 190 | { | |
| 191 | int i, r[3]; | |
| 192 | ||
| 193 | f->fL[0] += (f->lf * (s - f->fL[0]) + EQ_R) >> EQ_P; | |
| 194 | f->fH[0] += (f->hf * (s - f->fH[0]) + EQ_R) >> EQ_P; | |
| 195 | ||
| 196 | for (i = 1; i < 4; i++) { | |
| 197 | f->fL[i] += (f->lf * (f->fL[i - 1] - f->fL[i]) + EQ_R) >> EQ_P; | |
| 198 | f->fH[i] += (f->hf * (f->fH[i - 1] - f->fH[i]) + EQ_R) >> EQ_P; | |
| 199 | } | |
| 200 | ||
| 201 | r[0] = f->fL[3]; | |
| 202 | r[1] = f->fH[3] - f->fL[3]; | |
| 203 | r[2] = f->h[HISTOLD] - f->fH[3]; | |
| 204 | ||
| 205 | for (i = 0; i < 3; i++) { | |
| 206 | r[i] = (r[i] * f->g[i]) >> EQ_P; | |
| 207 | } | |
| 208 | ||
| 209 | for (i = HISTOLD; i > 0; i--) { | |
| 210 | f->h[i] = f->h[i - 1]; | |
| 211 | } | |
| 212 | f->h[HISTNEW] = s; | |
| 213 | ||
| 214 | return (r[0] + r[1] + r[2]); | |
| 215 | } | |
| 216 | ||
| 217 | #endif | |
| 218 | ||
| 219 | /*****************************************************************************/ | |
| 220 | /***************************** PUBLIC FUNCTIONS ******************************/ | |
| 221 | /*****************************************************************************/ | |
| 222 | ||
| 223 | extern void | |
| 224 | crt_resize(struct CRT *v, int w, int h, unsigned char *out) | |
| 225 | { | |
| 226 | v->outw = w; | |
| 227 | v->outh = h; | |
| 228 | v->out = out; | |
| 229 | } | |
| 230 | ||
| 231 | extern void | |
| 232 | crt_reset(struct CRT *v) | |
| 233 | { | |
| 234 | v->hue = 0; | |
| 235 | v->saturation = 10; | |
| 236 | v->brightness = 0; | |
| 237 | v->contrast = 180; | |
| 238 | v->black_point = 0; | |
| 239 | v->white_point = 100; | |
| 240 | v->hsync = 0; | |
| 241 | v->vsync = 0; | |
| 242 | ||
| 243 | v->scanlines = 0; /* leave gaps between lines if necessary */ | |
| 244 | v->blend = 0; /* blend new field onto previous image */ | |
| 245 | ||
| 246 | // these options were previously #defined in crt_core.h | |
| 247 | v->crt_do_vsync = 1; | |
| 248 | v->crt_do_hsync = 1; | |
| 249 | v->do_vhs_noise = 0; | |
| 250 | } | |
| 251 | ||
| 252 | extern void | |
| 253 | crt_init(struct CRT *v, int w, int h, unsigned char *out) | |
| 254 | { | |
| 255 | memset(v, 0, sizeof(struct CRT)); | |
| 256 | crt_resize(v, w, h, out); | |
| 257 | crt_reset(v); | |
| 258 | v->rn = 194; | |
| 259 | ||
| 260 | /* kilohertz to line sample conversion */ | |
| 261 | #define kHz2L(kHz) (CRT_HRES * (kHz * 100) / L_FREQ) | |
| 262 | ||
| 263 | /* band gains are pre-scaled as 16-bit fixed point | |
| 264 | * if you change the EQ_P define, you'll need to update these gains too | |
| 265 | */ | |
| 266 | #if (CRT_CC_SAMPLES == 4) | |
| 267 | init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 8192, 9175); | |
| 268 | init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); | |
| 269 | init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); | |
| 270 | #elif (CRT_CC_SAMPLES == 5) | |
| 271 | init_eq(&eqY, kHz2L(1500), kHz2L(3000), CRT_HRES, 65536, 12192, 7775); | |
| 272 | init_eq(&eqI, kHz2L(80), kHz2L(1150), CRT_HRES, 65536, 65536, 1311); | |
| 273 | init_eq(&eqQ, kHz2L(80), kHz2L(1000), CRT_HRES, 65536, 65536, 0); | |
| 274 | #else | |
| 275 | #error "NTSC-CRT currently only supports 4 or 5 samples per chroma period." | |
| 276 | #endif | |
| 277 | ||
| 278 | } | |
| 279 | ||
| 280 | extern void | |
| 281 | crt_demodulate(struct CRT *v, int noise) | |
| 282 | { | |
| 283 | /* made static so all this data does not go on the stack */ | |
| 284 | static struct { | |
| 285 | int y, i, q; | |
| 286 | } out[AV_LEN + 1], *yiqA, *yiqB; | |
| 287 | int i, j, line, rn; | |
| 288 | signed char *sig; | |
| 289 | int s = 0; | |
| 290 | int field, ratio; | |
| 291 | int *ccr; /* color carrier signal */ | |
| 292 | int huesn, huecs; | |
| 293 | int xnudge = -3, ynudge = 3; | |
| 294 | int bright = v->brightness - (BLACK_LEVEL + v->black_point); | |
| 295 | int pitch; | |
| 296 | ||
| 297 | pitch = v->outw * BPP; | |
| 298 | ||
| 299 | crt_sincos14(&huesn, &huecs, ((v->hue % 360) + 33) * 8192 / 180); | |
| 300 | huesn >>= 11; /* make 4-bit */ | |
| 301 | huecs >>= 11; | |
| 302 | ||
| 303 | rn = v->rn; | |
| 304 | if(!v->crt_do_vsync) | |
| 305 | { | |
| 306 | /* determine field before we add noise, | |
| 307 | * otherwise it's not reliably recoverable | |
| 308 | */ | |
| 309 | for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { | |
| 310 | line = POSMOD(v->vsync + i, CRT_VRES); | |
| 311 | sig = v->analog + line * CRT_HRES; | |
| 312 | s = 0; | |
| 313 | for (j = 0; j < CRT_HRES; j++) { | |
| 314 | s += sig[j]; | |
| 315 | if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { | |
| 316 | goto found_field; | |
| 317 | } | |
| 318 | } | |
| 319 | } | |
| 320 | found_field: | |
| 321 | /* if vsync signal was in second half of line, odd field */ | |
| 322 | field = (j > (CRT_HRES / 2)); | |
| 323 | v->vsync = -3; | |
| 324 | } | |
| 325 | if(v->do_vhs_noise) | |
| 326 | { | |
| 327 | line = ((rand() % 8) - 4) + 14; | |
| 328 | } | |
| 329 | for (i = 0; i < CRT_INPUT_SIZE; i++) { | |
| 330 | int nn = noise; | |
| 331 | if(v->do_vhs_noise) | |
| 332 | { | |
| 333 | rn = rand(); | |
| 334 | if (i > (CRT_INPUT_SIZE - CRT_HRES * (16 + ((rand() % 20) - 10))) && | |
| 335 | i < (CRT_INPUT_SIZE - CRT_HRES * (5 + ((rand() % 8) - 4)))) { | |
| 336 | int ln, sn, cs; | |
| 337 | ||
| 338 | ln = (i * line) / CRT_HRES; | |
| 339 | crt_sincos14(&sn, &cs, ln * 8192 / 180); | |
| 340 | nn = cs >> 8; | |
| 341 | } | |
| 342 | } | |
| 343 | else | |
| 344 | { | |
| 345 | rn = (214019 * rn + 140327895); | |
| 346 | } | |
| 347 | /* signal + noise */ | |
| 348 | s = v->analog[i] + (((((rn >> 16) & 0xff) - 0x7f) * nn) >> 8); | |
| 349 | if (s > 127) { s = 127; } | |
| 350 | if (s < -127) { s = -127; } | |
| 351 | v->inp[i] = s; | |
| 352 | } | |
| 353 | v->rn = rn; | |
| 354 | ||
| 355 | if(v->crt_do_vsync) | |
| 356 | { | |
| 357 | /* Look for vertical sync. | |
| 358 | * | |
| 359 | * This is done by integrating the signal and | |
| 360 | * seeing if it exceeds a threshold. The threshold of | |
| 361 | * the vertical sync pulse is much higher because the | |
| 362 | * vsync pulse is a lot longer than the hsync pulse. | |
| 363 | * The signal needs to be integrated to lessen | |
| 364 | * the noise in the signal. | |
| 365 | */ | |
| 366 | for (i = -CRT_VSYNC_WINDOW; i < CRT_VSYNC_WINDOW; i++) { | |
| 367 | line = POSMOD(v->vsync + i, CRT_VRES); | |
| 368 | sig = v->inp + line * CRT_HRES; | |
| 369 | s = 0; | |
| 370 | for (j = 0; j < CRT_HRES; j++) { | |
| 371 | s += sig[j]; | |
| 372 | /* increase the multiplier to make the vsync | |
| 373 | * more stable when there is a lot of noise | |
| 374 | */ | |
| 375 | if (s <= (CRT_VSYNC_THRESH * SYNC_LEVEL)) { | |
| 376 | goto vsync_found; | |
| 377 | } | |
| 378 | } | |
| 379 | } | |
| 380 | vsync_found: | |
| 381 | v->vsync = line; /* vsync found (or gave up) at this line */ | |
| 382 | /* if vsync signal was in second half of line, odd field */ | |
| 383 | field = (j > (CRT_HRES / 2)); | |
| 384 | } | |
| 385 | ||
| 386 | /* ratio of output height to active video lines in the signal */ | |
| 387 | ratio = (v->outh << 16) / CRT_LINES; | |
| 388 | ratio = (ratio + 32768) >> 16; | |
| 389 | ||
| 390 | field = (field * (ratio / 2)); | |
| 391 | ||
| 392 | for (line = CRT_TOP; line < CRT_BOT; line++) { | |
| 393 | unsigned pos, ln, scanR; | |
| 394 | int scanL, dx; | |
| 395 | int L, R; | |
| 396 | unsigned char *cL, *cR; | |
| 397 | #if (CRT_CC_SAMPLES == 4) | |
| 398 | int wave[CRT_CC_SAMPLES]; | |
| 399 | #else | |
| 400 | int waveI[CRT_CC_SAMPLES]; | |
| 401 | int waveQ[CRT_CC_SAMPLES]; | |
| 402 | #endif | |
| 403 | int dci, dcq; /* decoded I, Q */ | |
| 404 | int xpos, ypos; | |
| 405 | int beg, end; | |
| 406 | int phasealign; | |
| 407 | ||
| 408 | beg = (line - CRT_TOP + 0) * (v->outh + v->v_fac) / CRT_LINES + field; | |
| 409 | end = (line - CRT_TOP + 1) * (v->outh + v->v_fac) / CRT_LINES + field; | |
| 410 | ||
| 411 | if (beg >= v->outh) { continue; } | |
| 412 | if (end > v->outh) { end = v->outh; } | |
| 413 | ||
| 414 | /* Look for horizontal sync. | |
| 415 | * See comment above regarding vertical sync. | |
| 416 | */ | |
| 417 | ln = (POSMOD(line + v->vsync, CRT_VRES)) * CRT_HRES; | |
| 418 | sig = v->inp + ln + v->hsync; | |
| 419 | s = 0; | |
| 420 | for (i = -CRT_HSYNC_WINDOW; i < CRT_HSYNC_WINDOW; i++) { | |
| 421 | s += sig[SYNC_BEG + i]; | |
| 422 | if (s <= (CRT_HSYNC_THRESH * SYNC_LEVEL)) { | |
| 423 | break; | |
| 424 | } | |
| 425 | } | |
| 426 | if(v->crt_do_hsync) | |
| 427 | { | |
| 428 | v->hsync = POSMOD(i + v->hsync, CRT_HRES); | |
| 429 | } | |
| 430 | else | |
| 431 | { | |
| 432 | v->hsync = 0; | |
| 433 | } | |
| 434 | ||
| 435 | xpos = POSMOD(AV_BEG + v->hsync + xnudge, CRT_HRES); | |
| 436 | ypos = POSMOD(line + v->vsync + ynudge, CRT_VRES); | |
| 437 | pos = xpos + ypos * CRT_HRES; | |
| 438 | ||
| 439 | ccr = v->ccf[ypos % CRT_CC_VPER]; | |
| 440 | #if (CRT_CC_SAMPLES == 4) | |
| 441 | sig = v->inp + ln + (v->hsync & ~3); /* faster */ | |
| 442 | #else | |
| 443 | sig = v->inp + ln + (v->hsync - (v->hsync % CRT_CC_SAMPLES)); | |
| 444 | #endif | |
| 445 | for (i = CB_BEG; i < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); i++) { | |
| 446 | int p, n; | |
| 447 | p = ccr[i % CRT_CC_SAMPLES] * 127 / 128; /* fraction of the previous */ | |
| 448 | n = sig[i]; /* mixed with the new sample */ | |
| 449 | ccr[i % CRT_CC_SAMPLES] = p + n; | |
| 450 | } | |
| 451 | ||
| 452 | phasealign = POSMOD(v->hsync, CRT_CC_SAMPLES); | |
| 453 | ||
| 454 | #if (CRT_CC_SAMPLES == 4) | |
| 455 | /* amplitude of carrier = saturation, phase difference = hue */ | |
| 456 | dci = ccr[(phasealign + 1) & 3] - ccr[(phasealign + 3) & 3]; | |
| 457 | dcq = ccr[(phasealign + 2) & 3] - ccr[(phasealign + 0) & 3]; | |
| 458 | ||
| 459 | wave[0] = ((dci * huecs - dcq * huesn) >> 4) * v->saturation; | |
| 460 | wave[1] = ((dcq * huecs + dci * huesn) >> 4) * v->saturation; | |
| 461 | wave[2] = -wave[0]; | |
| 462 | wave[3] = -wave[1]; | |
| 463 | #elif (CRT_CC_SAMPLES == 5) | |
| 464 | { | |
| 465 | int dciA, dciB; | |
| 466 | int dcqA, dcqB; | |
| 467 | int ang = (v->hue % 360); | |
| 468 | int off180 = CRT_CC_SAMPLES / 2; | |
| 469 | int off90 = CRT_CC_SAMPLES / 4; | |
| 470 | int peakA = phasealign + off90; | |
| 471 | int peakB = phasealign + 0; | |
| 472 | dciA = dciB = dcqA = dcqB = 0; | |
| 473 | /* amplitude of carrier = saturation, phase difference = hue */ | |
| 474 | dciA = ccr[(peakA) % CRT_CC_SAMPLES]; | |
| 475 | /* average */ | |
| 476 | dciB = (ccr[(peakA + off180) % CRT_CC_SAMPLES] | |
| 477 | + ccr[(peakA + off180 + 1) % CRT_CC_SAMPLES]) / 2; | |
| 478 | dcqA = ccr[(peakB + off180) % CRT_CC_SAMPLES]; | |
| 479 | dcqB = ccr[(peakB) % CRT_CC_SAMPLES]; | |
| 480 | dci = dciA - dciB; | |
| 481 | dcq = dcqA - dcqB; | |
| 482 | /* create wave tables and rotate them by the hue adjustment angle */ | |
| 483 | for (i = 0; i < CRT_CC_SAMPLES; i++) { | |
| 484 | int sn, cs; | |
| 485 | crt_sincos14(&sn, &cs, ang * 8192 / 180); | |
| 486 | waveI[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; | |
| 487 | /* Q is offset by 90 */ | |
| 488 | crt_sincos14(&sn, &cs, (ang + 90) * 8192 / 180); | |
| 489 | waveQ[i] = ((dci * cs + dcq * sn) >> 15) * v->saturation; | |
| 490 | ang += (360 / CRT_CC_SAMPLES); | |
| 491 | } | |
| 492 | } | |
| 493 | #endif | |
| 494 | sig = v->inp + pos; | |
| 495 | ||
| 496 | dx = ((AV_LEN - 1) << 12) / v->outw; | |
| 497 | scanL = 0; | |
| 498 | scanR = (AV_LEN - 1) << 12; | |
| 499 | L = 0; | |
| 500 | R = AV_LEN; | |
| 501 | ||
| 502 | reset_eq(&eqY); | |
| 503 | reset_eq(&eqI); | |
| 504 | reset_eq(&eqQ); | |
| 505 | ||
| 506 | #if (CRT_CC_SAMPLES == 4) | |
| 507 | for (i = L; i < R; i++) { | |
| 508 | out[i].y = eqf(&eqY, sig[i] + bright) << 4; | |
| 509 | out[i].i = eqf(&eqI, sig[i] * wave[(i + 0) & 3] >> 9) >> 3; | |
| 510 | out[i].q = eqf(&eqQ, sig[i] * wave[(i + 3) & 3] >> 9) >> 3; | |
| 511 | } | |
| 512 | #else | |
| 513 | for (i = L; i < R; i++) { | |
| 514 | out[i].y = eqf(&eqY, sig[i] + bright) << 4; | |
| 515 | out[i].i = eqf(&eqI, sig[i] * waveI[i % CRT_CC_SAMPLES] >> 9) >> 3; | |
| 516 | out[i].q = eqf(&eqQ, sig[i] * waveQ[i % CRT_CC_SAMPLES] >> 9) >> 3; | |
| 517 | } | |
| 518 | #endif | |
| 519 | ||
| 520 | cL = v->out + (beg * pitch); | |
| 521 | cR = cL + pitch; | |
| 522 | ||
| 523 | for (pos = scanL; pos < scanR && cL < cR; pos += dx) { | |
| 524 | int y, i, q; | |
| 525 | int r, g, b; | |
| 526 | int aa, bb; | |
| 527 | ||
| 528 | R = pos & 0xfff; | |
| 529 | L = 0xfff - R; | |
| 530 | s = pos >> 12; | |
| 531 | ||
| 532 | yiqA = out + s; | |
| 533 | yiqB = out + s + 1; | |
| 534 | ||
| 535 | /* interpolate between samples if needed */ | |
| 536 | y = ((yiqA->y * L) >> 2) + ((yiqB->y * R) >> 2); | |
| 537 | i = ((yiqA->i * L) >> 14) + ((yiqB->i * R) >> 14); | |
| 538 | q = ((yiqA->q * L) >> 14) + ((yiqB->q * R) >> 14); | |
| 539 | ||
| 540 | /* YIQ to RGB */ | |
| 541 | r = (((y + 3879 * i + 2556 * q) >> 12) * v->contrast) >> 8; | |
| 542 | g = (((y - 1126 * i - 2605 * q) >> 12) * v->contrast) >> 8; | |
| 543 | b = (((y - 4530 * i + 7021 * q) >> 12) * v->contrast) >> 8; | |
| 544 | ||
| 545 | if (r < 0) r = 0; | |
| 546 | if (g < 0) g = 0; | |
| 547 | if (b < 0) b = 0; | |
| 548 | if (r > 255) r = 255; | |
| 549 | if (g > 255) g = 255; | |
| 550 | if (b > 255) b = 255; | |
| 551 | ||
| 552 | if (v->blend) { | |
| 553 | aa = (r << 16 | g << 8 | b); | |
| 554 | bb = cL[0] << 16 | cL[1] << 8 | cL[2]; | |
| 555 | ||
| 556 | ||
| 557 | /* blend with previous color there */ | |
| 558 | bb = (((aa & 0xfefeff) >> 1) + ((bb & 0xfefeff) >> 1)); | |
| 559 | } else { | |
| 560 | bb = (r << 16 | g << 8 | b); | |
| 561 | } | |
| 562 | ||
| 563 | cL[0] = bb >> 16 & 0xff; | |
| 564 | cL[1] = bb >> 8 & 0xff; | |
| 565 | cL[2] = bb >> 0 & 0xff; | |
| 566 | cL[3] = 0xff; | |
| 567 | ||
| 568 | cL += BPP; | |
| 569 | } | |
| 570 | ||
| 571 | /* duplicate extra lines */ | |
| 572 | for (s = beg + 1; s < (end - v->scanlines); s++) { | |
| 573 | memcpy(v->out + s * pitch, v->out + (s - 1) * pitch, pitch); | |
| 574 | } | |
| 575 | } | |
| 576 | } |
| 0 | /*****************************************************************************/ | |
| 1 | /* | |
| 2 | * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation | |
| 3 | * | |
| 4 | * by EMMIR 2018-2023 | |
| 5 | * | |
| 6 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos | |
| 7 | * Discord: https://discord.com/invite/hdYctSmyQJ | |
| 8 | */ | |
| 9 | /*****************************************************************************/ | |
| 10 | #ifndef _CRT_CORE_H_ | |
| 11 | #define _CRT_CORE_H_ | |
| 12 | ||
| 13 | #ifdef __cplusplus | |
| 14 | extern "C" { | |
| 15 | #endif | |
| 16 | ||
| 17 | /* crt_core.h | |
| 18 | * | |
| 19 | * The demodulator. This is also where you can define which system to emulate. | |
| 20 | * | |
| 21 | */ | |
| 22 | ||
| 23 | /* library version */ | |
| 24 | #define CRT_MAJOR 2 | |
| 25 | #define CRT_MINOR 3 | |
| 26 | #define CRT_PATCH 2 | |
| 27 | ||
| 28 | #include "crt_ntsc.h" | |
| 29 | ||
| 30 | // frei0r always uses 4 bits per pixel so it is pointless for there to be any other possibilities | |
| 31 | #define BPP 4 | |
| 32 | ||
| 33 | struct CRT { | |
| 34 | signed char analog[CRT_INPUT_SIZE]; | |
| 35 | signed char inp[CRT_INPUT_SIZE]; /* CRT input, can be noisy */ | |
| 36 | ||
| 37 | int outw, outh; /* output width/height */ | |
| 38 | unsigned char *out; /* output image */ | |
| 39 | ||
| 40 | int hue, brightness, contrast, saturation; /* common monitor settings */ | |
| 41 | int black_point, white_point; /* user-adjustable */ | |
| 42 | int scanlines; /* leave gaps between lines if necessary */ | |
| 43 | int blend; /* blend new field onto previous image */ | |
| 44 | unsigned v_fac; /* factor to stretch img vertically onto the output img */ | |
| 45 | ||
| 46 | // these options were previously #defined in crt_core.h | |
| 47 | int crt_do_vsync; | |
| 48 | int crt_do_hsync; | |
| 49 | int do_vhs_noise; /* 0 = no additional vhs noise, 1 = with additional vhs noise */ | |
| 50 | ||
| 51 | /* internal data */ | |
| 52 | int ccf[CRT_CC_VPER][CRT_CC_SAMPLES]; /* faster color carrier convergence */ | |
| 53 | int hsync, vsync; /* keep track of sync over frames */ | |
| 54 | int rn; /* seed for the 'random' noise */ | |
| 55 | }; | |
| 56 | ||
| 57 | /* Initializes the library. Sets up filters. | |
| 58 | * w - width of the output image | |
| 59 | * h - height of the output image | |
| 60 | * f - format of the output image | |
| 61 | * out - pointer to output image data | |
| 62 | */ | |
| 63 | extern void crt_init(struct CRT *v, int w, int h, unsigned char *out); | |
| 64 | ||
| 65 | /* Updates the output image parameters | |
| 66 | * w - width of the output image | |
| 67 | * h - height of the output image | |
| 68 | * f - format of the output image | |
| 69 | * out - pointer to output image data | |
| 70 | */ | |
| 71 | extern void crt_resize(struct CRT *v, int w, int h, unsigned char *out); | |
| 72 | ||
| 73 | /* Resets the CRT settings back to their defaults */ | |
| 74 | extern void crt_reset(struct CRT *v); | |
| 75 | ||
| 76 | /* Modulates RGB image into an analog NTSC signal | |
| 77 | * s - struct containing settings to apply to this field | |
| 78 | */ | |
| 79 | extern void crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s); | |
| 80 | ||
| 81 | /* Demodulates the NTSC signal generated by crt_modulate() | |
| 82 | * noise - the amount of noise added to the signal (0 - inf) | |
| 83 | */ | |
| 84 | extern void crt_demodulate(struct CRT *v, int noise); | |
| 85 | ||
| 86 | /*****************************************************************************/ | |
| 87 | /*************************** FIXED POINT SIN/COS *****************************/ | |
| 88 | /*****************************************************************************/ | |
| 89 | ||
| 90 | #define T14_2PI 16384 | |
| 91 | #define T14_MASK (T14_2PI - 1) | |
| 92 | #define T14_PI (T14_2PI / 2) | |
| 93 | ||
| 94 | extern void crt_sincos14(int *s, int *c, int n); | |
| 95 | ||
| 96 | #ifdef __cplusplus | |
| 97 | } | |
| 98 | #endif | |
| 99 | ||
| 100 | #endif |
| 0 | /*****************************************************************************/ | |
| 1 | /* | |
| 2 | * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation | |
| 3 | * | |
| 4 | * by EMMIR 2018-2023 | |
| 5 | * | |
| 6 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos | |
| 7 | * Discord: https://discord.com/invite/hdYctSmyQJ | |
| 8 | */ | |
| 9 | /*****************************************************************************/ | |
| 10 | ||
| 11 | #include "crt_core.h" | |
| 12 | ||
| 13 | #if (CRT_SYSTEM == CRT_SYSTEM_NTSCVHS) | |
| 14 | #include <stdlib.h> | |
| 15 | #include <string.h> | |
| 16 | ||
| 17 | #if (CRT_CHROMA_PATTERN == 1) | |
| 18 | /* 227.5 subcarrier cycles per line means every other line has reversed phase */ | |
| 19 | #define CC_PHASE(ln) (((ln) & 1) ? -1 : 1) | |
| 20 | #else | |
| 21 | #define CC_PHASE(ln) (1) | |
| 22 | #endif | |
| 23 | ||
| 24 | #define EXP_P 11 | |
| 25 | #define EXP_ONE (1 << EXP_P) | |
| 26 | #define EXP_MASK (EXP_ONE - 1) | |
| 27 | #define EXP_PI 6434 | |
| 28 | #define EXP_MUL(x, y) (((x) * (y)) >> EXP_P) | |
| 29 | #define EXP_DIV(x, y) (((x) << EXP_P) / (y)) | |
| 30 | ||
| 31 | static int e11[] = { | |
| 32 | EXP_ONE, | |
| 33 | 5567, /* e */ | |
| 34 | 15133, /* e^2 */ | |
| 35 | 41135, /* e^3 */ | |
| 36 | 111817 /* e^4 */ | |
| 37 | }; | |
| 38 | ||
| 39 | /* fixed point e^x */ | |
| 40 | static int | |
| 41 | expx(int n) | |
| 42 | { | |
| 43 | int neg, idx, res; | |
| 44 | int nxt, acc, del; | |
| 45 | int i; | |
| 46 | ||
| 47 | if (n == 0) { | |
| 48 | return EXP_ONE; | |
| 49 | } | |
| 50 | neg = n < 0; | |
| 51 | if (neg) { | |
| 52 | n = -n; | |
| 53 | } | |
| 54 | idx = n >> EXP_P; | |
| 55 | res = EXP_ONE; | |
| 56 | for (i = 0; i < idx / 4; i++) { | |
| 57 | res = EXP_MUL(res, e11[4]); | |
| 58 | } | |
| 59 | idx &= 3; | |
| 60 | if (idx > 0) { | |
| 61 | res = EXP_MUL(res, e11[idx]); | |
| 62 | } | |
| 63 | ||
| 64 | n &= EXP_MASK; | |
| 65 | nxt = EXP_ONE; | |
| 66 | acc = 0; | |
| 67 | del = 1; | |
| 68 | for (i = 1; i < 17; i++) { | |
| 69 | acc += nxt / del; | |
| 70 | nxt = EXP_MUL(nxt, n); | |
| 71 | del *= i; | |
| 72 | if (del > nxt || nxt <= 0 || del <= 0) { | |
| 73 | break; | |
| 74 | } | |
| 75 | } | |
| 76 | res = EXP_MUL(res, acc); | |
| 77 | ||
| 78 | if (neg) { | |
| 79 | res = EXP_DIV(EXP_ONE, res); | |
| 80 | } | |
| 81 | return res; | |
| 82 | } | |
| 83 | ||
| 84 | /*****************************************************************************/ | |
| 85 | /********************************* FILTERS ***********************************/ | |
| 86 | /*****************************************************************************/ | |
| 87 | ||
| 88 | /* infinite impulse response low pass filter for bandlimiting YIQ */ | |
| 89 | static struct IIRLP { | |
| 90 | int c; | |
| 91 | int h; /* history */ | |
| 92 | } iirY, iirI, iirQ; | |
| 93 | ||
| 94 | /* freq - total bandwidth | |
| 95 | * limit - max frequency | |
| 96 | */ | |
| 97 | static void | |
| 98 | init_iir(struct IIRLP *f, int freq, int limit) | |
| 99 | { | |
| 100 | int rate; /* cycles/pixel rate */ | |
| 101 | ||
| 102 | memset(f, 0, sizeof(struct IIRLP)); | |
| 103 | rate = (freq << 9) / limit; | |
| 104 | f->c = EXP_ONE - expx(-((EXP_PI << 9) / rate)); | |
| 105 | } | |
| 106 | ||
| 107 | static void | |
| 108 | reset_iir(struct IIRLP *f) | |
| 109 | { | |
| 110 | f->h = 0; | |
| 111 | } | |
| 112 | ||
| 113 | /* hi-pass for debugging */ | |
| 114 | #define HIPASS 0 | |
| 115 | ||
| 116 | static int | |
| 117 | iirf(struct IIRLP *f, int s) | |
| 118 | { | |
| 119 | f->h += EXP_MUL(s - f->h, f->c); | |
| 120 | #if HIPASS | |
| 121 | return s - f->h; | |
| 122 | #else | |
| 123 | return f->h; | |
| 124 | #endif | |
| 125 | } | |
| 126 | ||
| 127 | extern void | |
| 128 | crt_modulate(struct CRT *v, struct NTSC_SETTINGS *s) | |
| 129 | { | |
| 130 | int x, y, xo, yo; | |
| 131 | int destw = AV_LEN; | |
| 132 | int desth = ((CRT_LINES * 64500) >> 16); | |
| 133 | int iccf[CRT_CC_SAMPLES]; | |
| 134 | int ccmodI[CRT_CC_SAMPLES]; /* color phase for mod */ | |
| 135 | int ccmodQ[CRT_CC_SAMPLES]; /* color phase for mod */ | |
| 136 | int ccburst[CRT_CC_SAMPLES]; /* color phase for burst */ | |
| 137 | int sn, cs, n, ph; | |
| 138 | int inv_phase = 0; | |
| 139 | int aberration = 0; | |
| 140 | ||
| 141 | if (!s->iirs_initialized) { | |
| 142 | int y_freq = Y_FREQ_OFF; | |
| 143 | int i_freq = I_FREQ_OFF; | |
| 144 | int q_freq = Q_FREQ_OFF; | |
| 145 | ||
| 146 | switch(s->vhs_mode) | |
| 147 | { | |
| 148 | case VHS_OFF: | |
| 149 | break; | |
| 150 | case VHS_SP: | |
| 151 | y_freq = Y_FREQ_SP; | |
| 152 | i_freq = I_FREQ_SP; | |
| 153 | q_freq = Q_FREQ_SP; | |
| 154 | break; | |
| 155 | case VHS_LP: | |
| 156 | y_freq = Y_FREQ_LP; | |
| 157 | i_freq = I_FREQ_LP; | |
| 158 | q_freq = Q_FREQ_LP; | |
| 159 | break; | |
| 160 | case VHS_EP: | |
| 161 | y_freq = Y_FREQ_EP; | |
| 162 | i_freq = I_FREQ_EP; | |
| 163 | q_freq = Q_FREQ_EP; | |
| 164 | break; | |
| 165 | default: | |
| 166 | break; | |
| 167 | } | |
| 168 | ||
| 169 | init_iir(&iirY, L_FREQ, y_freq); | |
| 170 | init_iir(&iirI, L_FREQ, i_freq); | |
| 171 | init_iir(&iirQ, L_FREQ, q_freq); | |
| 172 | s->iirs_initialized = 1; | |
| 173 | } | |
| 174 | if (s->raw) { | |
| 175 | destw = s->w; | |
| 176 | desth = s->h; | |
| 177 | if (destw > AV_LEN) { | |
| 178 | destw = AV_LEN; | |
| 179 | } | |
| 180 | if (desth > ((CRT_LINES * 64500) >> 16)) { | |
| 181 | desth = ((CRT_LINES * 64500) >> 16); | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | if (s->as_color) { | |
| 186 | for (x = 0; x < CRT_CC_SAMPLES; x++) { | |
| 187 | n = s->hue + x * (360 / CRT_CC_SAMPLES); | |
| 188 | crt_sincos14(&sn, &cs, (n + 33) * 8192 / 180); | |
| 189 | ccburst[x] = sn >> 10; | |
| 190 | crt_sincos14(&sn, &cs, n * 8192 / 180); | |
| 191 | ccmodI[x] = sn >> 10; | |
| 192 | crt_sincos14(&sn, &cs, (n - 90) * 8192 / 180); | |
| 193 | ccmodQ[x] = sn >> 10; | |
| 194 | } | |
| 195 | } else { | |
| 196 | memset(ccburst, 0, sizeof(ccburst)); | |
| 197 | memset(ccmodI, 0, sizeof(ccmodI)); | |
| 198 | memset(ccmodQ, 0, sizeof(ccmodQ)); | |
| 199 | } | |
| 200 | ||
| 201 | xo = AV_BEG + s->xoffset + (AV_LEN - destw) / 2; | |
| 202 | yo = CRT_TOP + s->yoffset + (CRT_LINES - desth) / 2; | |
| 203 | ||
| 204 | s->field &= 1; | |
| 205 | s->frame &= 1; | |
| 206 | inv_phase = (s->field == s->frame); | |
| 207 | ph = CC_PHASE(inv_phase); | |
| 208 | ||
| 209 | /* align signal */ | |
| 210 | xo = (xo & ~3); | |
| 211 | if (s->do_aberration) { | |
| 212 | aberration = ((rand() % 12) - 8) + 14; | |
| 213 | } | |
| 214 | for (n = 0; n < CRT_VRES; n++) { | |
| 215 | int t; /* time */ | |
| 216 | signed char *line = &v->analog[n * CRT_HRES]; | |
| 217 | ||
| 218 | t = LINE_BEG; | |
| 219 | ||
| 220 | if (n <= 3 || (n >= 7 && n <= 9)) { | |
| 221 | /* equalizing pulses - small blips of sync, mostly blank */ | |
| 222 | while (t < (4 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; | |
| 223 | while (t < (50 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; | |
| 224 | while (t < (54 * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; | |
| 225 | while (t < (100 * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; | |
| 226 | } else if (n >= 4 && n <= 6) { | |
| 227 | int even[4] = { 46, 50, 96, 100 }; | |
| 228 | int odd[4] = { 4, 50, 96, 100 }; | |
| 229 | int *offs = even; | |
| 230 | if (s->field == 1) { | |
| 231 | offs = odd; | |
| 232 | } | |
| 233 | /* vertical sync pulse - small blips of blank, mostly sync */ | |
| 234 | while (t < (offs[0] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; | |
| 235 | while (t < (offs[1] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; | |
| 236 | while (t < (offs[2] * CRT_HRES / 100)) line[t++] = SYNC_LEVEL; | |
| 237 | while (t < (offs[3] * CRT_HRES / 100)) line[t++] = BLANK_LEVEL; | |
| 238 | } else { | |
| 239 | int cb; | |
| 240 | if (n < (CRT_VRES - aberration)) { | |
| 241 | /* video line */ | |
| 242 | while (t < SYNC_BEG) line[t++] = BLANK_LEVEL; /* FP */ | |
| 243 | while (t < BW_BEG) line[t++] = SYNC_LEVEL; /* SYNC */ | |
| 244 | } | |
| 245 | while (t < AV_BEG) line[t++] = BLANK_LEVEL; /* BW + CB + BP */ | |
| 246 | ||
| 247 | if (n < CRT_TOP) { | |
| 248 | while (t < CRT_HRES) line[t++] = BLANK_LEVEL; | |
| 249 | } | |
| 250 | ||
| 251 | /* CB_CYCLES of color burst at 3.579545 Mhz */ | |
| 252 | for (t = CB_BEG; t < CB_BEG + (CB_CYCLES * CRT_CB_FREQ); t++) { | |
| 253 | #if (CRT_CHROMA_PATTERN == 1) | |
| 254 | int off180 = CRT_CC_SAMPLES / 2; | |
| 255 | cb = ccburst[(t + inv_phase * off180) % CRT_CC_SAMPLES]; | |
| 256 | #else | |
| 257 | cb = ccburst[t % CRT_CC_SAMPLES]; | |
| 258 | #endif | |
| 259 | line[t] = (BLANK_LEVEL + (cb * BURST_LEVEL)) >> 5; | |
| 260 | iccf[t % CRT_CC_SAMPLES] = line[t]; | |
| 261 | } | |
| 262 | } | |
| 263 | } | |
| 264 | ||
| 265 | if(s->vhs_mode != VHS_OFF) | |
| 266 | { | |
| 267 | /* reset hsync every frame so only the bottom part is warped */ | |
| 268 | v->hsync = 0; | |
| 269 | } | |
| 270 | ||
| 271 | for (y = 0; y < desth; y++) { | |
| 272 | int field_offset; | |
| 273 | int sy; | |
| 274 | ||
| 275 | field_offset = (s->field * s->h + desth) / desth / 2; | |
| 276 | sy = (y * s->h) / desth; | |
| 277 | ||
| 278 | sy += field_offset; | |
| 279 | ||
| 280 | if (sy >= s->h) sy = s->h; | |
| 281 | ||
| 282 | sy *= s->w; | |
| 283 | ||
| 284 | reset_iir(&iirY); | |
| 285 | reset_iir(&iirI); | |
| 286 | reset_iir(&iirQ); | |
| 287 | ||
| 288 | for (x = 0; x < destw; x++) { | |
| 289 | int fy, fi, fq; | |
| 290 | int rA, gA, bA; | |
| 291 | const unsigned char *pix; | |
| 292 | int ire; /* composite signal */ | |
| 293 | int xoff; | |
| 294 | ||
| 295 | pix = s->data + ((((x * s->w) / destw) + sy) * BPP); | |
| 296 | rA = pix[0]; | |
| 297 | gA = pix[1]; | |
| 298 | bA = pix[2]; | |
| 299 | ||
| 300 | ||
| 301 | /* RGB to YIQ */ | |
| 302 | fy = (19595 * rA + 38470 * gA + 7471 * bA) >> 14; | |
| 303 | fi = (39059 * rA - 18022 * gA - 21103 * bA) >> 14; | |
| 304 | fq = (13894 * rA - 34275 * gA + 20382 * bA) >> 14; | |
| 305 | ire = BLACK_LEVEL + v->black_point; | |
| 306 | ||
| 307 | xoff = (x + xo) % CRT_CC_SAMPLES; | |
| 308 | /* bandlimit Y,I,Q */ | |
| 309 | fy = iirf(&iirY, fy); | |
| 310 | fi = iirf(&iirI, fi) * ph * ccmodI[xoff] >> 4; | |
| 311 | fq = iirf(&iirQ, fq) * ph * ccmodQ[xoff] >> 4; | |
| 312 | ire += (fy + fi + fq) * (WHITE_LEVEL * v->white_point / 100) >> 10; | |
| 313 | if (ire < 0) ire = 0; | |
| 314 | if (ire > 110) ire = 110; | |
| 315 | ||
| 316 | v->analog[(x + xo) + (y + yo) * CRT_HRES] = ire; | |
| 317 | } | |
| 318 | } | |
| 319 | for (n = 0; n < CRT_CC_VPER; n++) { | |
| 320 | for (x = 0; x < CRT_CC_SAMPLES; x++) { | |
| 321 | if(s->vhs_mode != VHS_OFF) | |
| 322 | { | |
| 323 | v->ccf[n][x] = 0; | |
| 324 | } | |
| 325 | else | |
| 326 | { | |
| 327 | v->ccf[n][x] = iccf[x] << 7; | |
| 328 | } | |
| 329 | } | |
| 330 | } | |
| 331 | } | |
| 332 | #endif |
| 0 | /*****************************************************************************/ | |
| 1 | /* | |
| 2 | * NTSC/CRT - integer-only NTSC video signal encoding / decoding emulation | |
| 3 | * | |
| 4 | * by EMMIR 2018-2023 | |
| 5 | * | |
| 6 | * YouTube: https://www.youtube.com/@EMMIR_KC/videos | |
| 7 | * Discord: https://discord.com/invite/hdYctSmyQJ | |
| 8 | */ | |
| 9 | /*****************************************************************************/ | |
| 10 | #ifndef _CRT_NTSC_VHS_H_ | |
| 11 | #define _CRT_NTSC_VHS_H_ | |
| 12 | ||
| 13 | #ifdef __cplusplus | |
| 14 | extern "C" { | |
| 15 | #endif | |
| 16 | ||
| 17 | /* crt_ntscvhs.h | |
| 18 | * | |
| 19 | * An interface to convert a digital image to an analog NTSC signal with VHS | |
| 20 | * quality and some optional signal aberration at the bottom. | |
| 21 | * | |
| 22 | */ | |
| 23 | /* 0 = vertical chroma (228 chroma clocks per line) */ | |
| 24 | /* 1 = checkered chroma (227.5 chroma clocks per line) */ | |
| 25 | #define CRT_CHROMA_PATTERN 1 | |
| 26 | ||
| 27 | /* chroma clocks (subcarrier cycles) per line */ | |
| 28 | #if (CRT_CHROMA_PATTERN == 1) | |
| 29 | #define CRT_CC_LINE 2275 | |
| 30 | #else | |
| 31 | /* this will give the 'rainbow' effect in the famous waterfall scene */ | |
| 32 | #define CRT_CC_LINE 2280 | |
| 33 | #endif | |
| 34 | ||
| 35 | /* NOTE, in general, increasing CRT_CB_FREQ reduces blur and bleed */ | |
| 36 | #define CRT_CB_FREQ 4 /* carrier frequency relative to sample rate */ | |
| 37 | #define CRT_HRES (CRT_CC_LINE * CRT_CB_FREQ / 10) /* horizontal res */ | |
| 38 | #define CRT_VRES 262 /* vertical resolution */ | |
| 39 | #define CRT_INPUT_SIZE (CRT_HRES * CRT_VRES) | |
| 40 | ||
| 41 | #define CRT_TOP 21 /* first line with active video */ | |
| 42 | #define CRT_BOT 261 /* final line with active video */ | |
| 43 | #define CRT_LINES (CRT_BOT - CRT_TOP) /* number of active video lines */ | |
| 44 | ||
| 45 | #define CRT_CC_SAMPLES 4 /* samples per chroma period (samples per 360 deg) */ | |
| 46 | #define CRT_CC_VPER 1 /* vertical period in which the artifacts repeat */ | |
| 47 | ||
| 48 | /* search windows, in samples */ | |
| 49 | #define CRT_HSYNC_WINDOW 8 | |
| 50 | #define CRT_VSYNC_WINDOW 8 | |
| 51 | ||
| 52 | /* accumulated signal threshold required for sync detection. | |
| 53 | * Larger = more stable, until it's so large that it is never reached in which | |
| 54 | * case the CRT won't be able to sync | |
| 55 | */ | |
| 56 | #define CRT_HSYNC_THRESH 4 | |
| 57 | #define CRT_VSYNC_THRESH 94 | |
| 58 | ||
| 59 | /* | |
| 60 | * FULL HORIZONTAL LINE SIGNAL (~63500 ns) | |
| 61 | * |---------------------------------------------------------------------------| | |
| 62 | * HBLANK (~10900 ns) ACTIVE VIDEO (~52600 ns) | |
| 63 | * |-------------------||------------------------------------------------------| | |
| 64 | * | |
| 65 | * | |
| 66 | * WITHIN HBLANK PERIOD: | |
| 67 | * | |
| 68 | * FP (~1500 ns) SYNC (~4700 ns) BW (~600 ns) CB (~2500 ns) BP (~1600 ns) | |
| 69 | * |--------------||---------------||------------||-------------||-------------| | |
| 70 | * BLANK SYNC BLANK BLANK BLANK | |
| 71 | * | |
| 72 | */ | |
| 73 | #define LINE_BEG 0 | |
| 74 | #define FP_ns 1500 /* front porch */ | |
| 75 | #define SYNC_ns 4700 /* sync tip */ | |
| 76 | #define BW_ns 600 /* breezeway */ | |
| 77 | #define CB_ns 2500 /* color burst */ | |
| 78 | #define BP_ns 1600 /* back porch */ | |
| 79 | #define AV_ns 52600 /* active video */ | |
| 80 | #define HB_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns) /* h blank */ | |
| 81 | /* line duration should be ~63500 ns */ | |
| 82 | #define LINE_ns (FP_ns + SYNC_ns + BW_ns + CB_ns + BP_ns + AV_ns) | |
| 83 | ||
| 84 | /* convert nanosecond offset to its corresponding point on the sampled line */ | |
| 85 | #define ns2pos(ns) ((ns) * CRT_HRES / LINE_ns) | |
| 86 | /* starting points for all the different pulses */ | |
| 87 | #define FP_BEG ns2pos(0) | |
| 88 | #define SYNC_BEG ns2pos(FP_ns) | |
| 89 | #define BW_BEG ns2pos(FP_ns + SYNC_ns) | |
| 90 | #define CB_BEG ns2pos(FP_ns + SYNC_ns + BW_ns) | |
| 91 | #define BP_BEG ns2pos(FP_ns + SYNC_ns + BW_ns + CB_ns) | |
| 92 | #define AV_BEG ns2pos(HB_ns) | |
| 93 | #define AV_LEN ns2pos(AV_ns) | |
| 94 | ||
| 95 | /* somewhere between 7 and 12 cycles */ | |
| 96 | #define CB_CYCLES 10 | |
| 97 | ||
| 98 | #define VHS_OFF 0 | |
| 99 | #define VHS_SP 1 | |
| 100 | #define VHS_LP 2 | |
| 101 | #define VHS_EP 3 | |
| 102 | ||
| 103 | #define VHS_MODE VHS_OFF | |
| 104 | ||
| 105 | /* frequencies for bandlimiting */ | |
| 106 | #define L_FREQ 1431818 /* full line */ | |
| 107 | ||
| 108 | #define Y_FREQ_OFF 420000 /* Luma (Y) 4.2 MHz of the 14.31818 MHz */ | |
| 109 | #define I_FREQ_OFF 150000 /* Chroma (I) 1.5 MHz of the 14.31818 MHz */ | |
| 110 | #define Q_FREQ_OFF 55000 /* Chroma (Q) 0.55 MHz of the 14.31818 MHz */ | |
| 111 | ||
| 112 | #define Y_FREQ_SP 300000 /* Luma (Y) 3.0 MHz of the 14.31818 MHz */ | |
| 113 | #define I_FREQ_SP 62700 /* Chroma (I) 627 kHz of the 14.31818 MHz */ | |
| 114 | #define Q_FREQ_SP 62700 /* Chroma (Q) 627 kHz of the 14.31818 MHz */ | |
| 115 | ||
| 116 | #define Y_FREQ_LP 240000 /* Luma (Y) 2.4 MHz of the 14.31818 MHz */ | |
| 117 | #define I_FREQ_LP 40000 /* Chroma (I) 400 kHz of the 14.31818 MHz */ | |
| 118 | #define Q_FREQ_LP 40000 /* Chroma (Q) 400 kHz of the 14.31818 MHz */ | |
| 119 | ||
| 120 | #define Y_FREQ_EP 200000 /* Luma (Y) 2.0 MHz of the 14.31818 MHz */ | |
| 121 | #define I_FREQ_EP 37000 /* Chroma (I) 370 kHz of the 14.31818 MHz */ | |
| 122 | #define Q_FREQ_EP 37000 /* Chroma (Q) 370 kHz of the 14.31818 MHz */ | |
| 123 | ||
| 124 | ||
| 125 | /* IRE units (100 = 1.0V, -40 = 0.0V) */ | |
| 126 | #define WHITE_LEVEL 100 | |
| 127 | #define BURST_LEVEL 20 | |
| 128 | #define BLACK_LEVEL 7 | |
| 129 | #define BLANK_LEVEL 0 | |
| 130 | #define SYNC_LEVEL -40 | |
| 131 | ||
| 132 | struct NTSC_SETTINGS { | |
| 133 | const unsigned char *data; /* image data */ | |
| 134 | int w, h; /* width and height of image */ | |
| 135 | int raw; /* 0 = scale image to fit monitor, 1 = don't scale */ | |
| 136 | int as_color; /* 0 = monochrome, 1 = full color */ | |
| 137 | int field; /* 0 = even, 1 = odd */ | |
| 138 | int frame; /* 0 = even, 1 = odd */ | |
| 139 | int hue; /* 0-359 */ | |
| 140 | int xoffset; /* x offset in sample space. 0 is minimum value */ | |
| 141 | int yoffset; /* y offset in # of lines. 0 is minimum value */ | |
| 142 | int do_aberration; /* 0 = no aberration, 1 = with aberration */ | |
| 143 | ||
| 144 | // these are changed by the vhs mode | |
| 145 | int vhs_mode; | |
| 146 | int y_freq; | |
| 147 | int i_freq; | |
| 148 | int q_freq; | |
| 149 | ||
| 150 | /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ | |
| 151 | int iirs_initialized; /* internal state */ | |
| 152 | }; | |
| 153 | ||
| 154 | #ifdef __cplusplus | |
| 155 | } | |
| 156 | #endif | |
| 157 | ||
| 158 | #endif |
| 0 | #include <stdlib.h> | |
| 1 | ||
| 2 | #include "frei0r.h" | |
| 3 | #include "frei0r/math.h" | |
| 4 | ||
| 5 | #include "crt_core.h" | |
| 6 | ||
| 7 | // the actual NTSC emulation code is modified from here: https://github.com/LMP88959/NTSC-CRT | |
| 8 | ||
| 9 | typedef struct ntsc_instance | |
| 10 | { | |
| 11 | // image dimensions | |
| 12 | int width; | |
| 13 | int height; | |
| 14 | ||
| 15 | // parameters | |
| 16 | struct CRT crt; | |
| 17 | struct NTSC_SETTINGS ntsc_settings; | |
| 18 | ||
| 19 | int noise; | |
| 20 | double vhs_speed; | |
| 21 | ||
| 22 | int field; | |
| 23 | int progressive; | |
| 24 | ||
| 25 | } ntsc_instance_t; | |
| 26 | ||
| 27 | // these functions are for frei0r | |
| 28 | // mostly copy/paste/slightly modified from the other frei0r effects | |
| 29 | int f0r_init() | |
| 30 | { | |
| 31 | return 1; | |
| 32 | } | |
| 33 | ||
| 34 | void f0r_deinit() | |
| 35 | {} | |
| 36 | ||
| 37 | void f0r_get_plugin_info(f0r_plugin_info_t* info) | |
| 38 | { | |
| 39 | info->name = "NTSC"; | |
| 40 | info->author = "EMMIR, esmane"; | |
| 41 | info->explanation = "Simulates NTSC analog video."; | |
| 42 | info->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 43 | info->color_model = F0R_COLOR_MODEL_RGBA8888; | |
| 44 | info->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 45 | info->major_version = 0; | |
| 46 | info->minor_version = 1; | |
| 47 | info->num_params = 8; | |
| 48 | } | |
| 49 | ||
| 50 | void f0r_get_param_info(f0r_param_info_t* info, int param_index) | |
| 51 | { | |
| 52 | switch(param_index) | |
| 53 | { | |
| 54 | case 0: | |
| 55 | info->name = "Signal Noise"; | |
| 56 | info->explanation = "Amount of noise introduced into the NTSC signal."; | |
| 57 | info->type = F0R_PARAM_DOUBLE; | |
| 58 | break; | |
| 59 | ||
| 60 | case 1: | |
| 61 | info->name = "VHS Speed"; | |
| 62 | info->explanation = "Simulates VHS. 0 = off, 1 = SP, 2 = LP, 3 = EP"; | |
| 63 | info->type = F0R_PARAM_DOUBLE; | |
| 64 | break; | |
| 65 | ||
| 66 | case 2: | |
| 67 | info->name = "VHS Noise"; | |
| 68 | info->explanation = "Toggles VHS noise at the bottom of the screen"; | |
| 69 | info->type = F0R_PARAM_BOOL; | |
| 70 | break; | |
| 71 | ||
| 72 | case 3: | |
| 73 | info->name = "Aberration"; | |
| 74 | info->explanation = "Toggles VHS aberration at the bottom of the screen. Not visible if V-sync is on."; | |
| 75 | info->type = F0R_PARAM_BOOL; | |
| 76 | break; | |
| 77 | ||
| 78 | case 4: | |
| 79 | info->name = "Force V-sync"; | |
| 80 | info->explanation = "Forces V-sync even when the signal is really noisy."; | |
| 81 | info->type = F0R_PARAM_BOOL; | |
| 82 | break; | |
| 83 | ||
| 84 | case 5: | |
| 85 | info->name = "Force H-sync"; | |
| 86 | info->explanation = "Forces V-sync even when the signal is really noisy."; | |
| 87 | info->type = F0R_PARAM_BOOL; | |
| 88 | break; | |
| 89 | ||
| 90 | case 6: | |
| 91 | info->name = "Progressive Scan"; | |
| 92 | info->explanation = "Toggles progressive scan (Interlaced if off)."; | |
| 93 | info->type = F0R_PARAM_BOOL; | |
| 94 | break; | |
| 95 | ||
| 96 | case 7: | |
| 97 | info->name = "Blend"; | |
| 98 | info->explanation = "Blends frames."; | |
| 99 | info->type = F0R_PARAM_BOOL; | |
| 100 | break; | |
| 101 | } | |
| 102 | } | |
| 103 | ||
| 104 | f0r_instance_t f0r_construct(unsigned int width, unsigned int height) | |
| 105 | { | |
| 106 | ntsc_instance_t* inst = (ntsc_instance_t*)calloc(1, sizeof(*inst)); | |
| 107 | ||
| 108 | inst->width = width; | |
| 109 | inst->height = height; | |
| 110 | ||
| 111 | inst->vhs_speed = 0.0; | |
| 112 | inst->field = 0; | |
| 113 | inst->progressive = 1; | |
| 114 | ||
| 115 | crt_init(&(inst->crt), width, height, NULL); | |
| 116 | ||
| 117 | // init NTSC_SETTINGS | |
| 118 | inst->ntsc_settings.data = NULL; | |
| 119 | inst->ntsc_settings.w = width; | |
| 120 | inst->ntsc_settings.h = height; | |
| 121 | inst->ntsc_settings.raw = 0; | |
| 122 | inst->ntsc_settings.as_color = 1; | |
| 123 | inst->ntsc_settings.field = 0; | |
| 124 | inst->ntsc_settings.frame = 0; | |
| 125 | inst->ntsc_settings.hue = 0; | |
| 126 | inst->ntsc_settings.xoffset = 0; | |
| 127 | inst->ntsc_settings.yoffset = 0; | |
| 128 | inst->ntsc_settings.do_aberration = 0; | |
| 129 | ||
| 130 | // these are changed by the vhs mode | |
| 131 | inst->ntsc_settings.vhs_mode = 0; | |
| 132 | inst->ntsc_settings.y_freq = 0; | |
| 133 | inst->ntsc_settings.i_freq = 0; | |
| 134 | inst->ntsc_settings.q_freq = 0; | |
| 135 | ||
| 136 | /* make sure your NTSC_SETTINGS struct is zeroed out before you do anything */ | |
| 137 | inst->ntsc_settings.iirs_initialized = 0; | |
| 138 | ||
| 139 | inst->noise = 0; | |
| 140 | ||
| 141 | return (f0r_instance_t)inst; | |
| 142 | } | |
| 143 | ||
| 144 | void f0r_destruct(f0r_instance_t instance) | |
| 145 | { | |
| 146 | ntsc_instance_t* inst = (ntsc_instance_t*)instance; | |
| 147 | free(instance); | |
| 148 | } | |
| 149 | ||
| 150 | ||
| 151 | void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) | |
| 152 | { | |
| 153 | ntsc_instance_t* inst = (ntsc_instance_t*)instance; | |
| 154 | switch(param_index) | |
| 155 | { | |
| 156 | case 0: | |
| 157 | inst->noise = *((double*)param) * 100; | |
| 158 | break; | |
| 159 | case 1: | |
| 160 | inst->ntsc_settings.vhs_mode = (int)CLAMP(*((double*)param) * 4, 0, 4); | |
| 161 | inst->ntsc_settings.iirs_initialized = 0; | |
| 162 | break; | |
| 163 | case 2: | |
| 164 | inst->crt.do_vhs_noise = (*((double*)param) >= 0.5); | |
| 165 | break; | |
| 166 | case 3: | |
| 167 | inst->ntsc_settings.do_aberration = (*((double*)param) >= 0.5); | |
| 168 | break; | |
| 169 | case 4: | |
| 170 | inst->crt.crt_do_vsync = !(*((double*)param) >= 0.5); | |
| 171 | break; | |
| 172 | case 5: | |
| 173 | inst->crt.crt_do_hsync = !(*((double*)param) >= 0.5); | |
| 174 | break; | |
| 175 | case 6: | |
| 176 | inst->progressive = (*((double*)param) >= 0.5); | |
| 177 | break; | |
| 178 | case 7: | |
| 179 | inst->crt.blend = (*((double*)param) >= 0.5); | |
| 180 | break; | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) | |
| 185 | { | |
| 186 | ntsc_instance_t* inst = (ntsc_instance_t*)instance; | |
| 187 | switch(param_index) | |
| 188 | { | |
| 189 | case 0: | |
| 190 | *((double*)param) = (inst->noise / 100); | |
| 191 | break; | |
| 192 | case 1: | |
| 193 | *((double*)param) = (inst->ntsc_settings.vhs_mode / 4); | |
| 194 | break; | |
| 195 | case 2: | |
| 196 | *((double*)param) = (inst->crt.do_vhs_noise ? 1.0 : 0.0); | |
| 197 | break; | |
| 198 | case 3: | |
| 199 | *((double*)param) = (inst->ntsc_settings.do_aberration ? 1.0 : 0.0); | |
| 200 | break; | |
| 201 | case 4: | |
| 202 | *((double*)param) = !(inst->crt.crt_do_vsync ? 1.0 : 0.0); | |
| 203 | break; | |
| 204 | case 5: | |
| 205 | *((double*)param) = !(inst->crt.crt_do_hsync ? 1.0 : 0.0); | |
| 206 | break; | |
| 207 | case 6: | |
| 208 | *((double*)param) = (inst->progressive ? 1.0 : 0.0); | |
| 209 | break; | |
| 210 | case 7: | |
| 211 | *((double*)param) = (inst->crt.blend ? 1.0 : 0.0); | |
| 212 | break; | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | ||
| 217 | void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe) | |
| 218 | { | |
| 219 | ntsc_instance_t* inst = (ntsc_instance_t*)instance; | |
| 220 | ||
| 221 | inst->crt.out = (char*)outframe; | |
| 222 | inst->ntsc_settings.data = (const char*)inframe; | |
| 223 | ||
| 224 | inst->ntsc_settings.field = inst->field & 1; | |
| 225 | if (inst->ntsc_settings.field == 0) { | |
| 226 | /* a frame is two fields */ | |
| 227 | inst->ntsc_settings.frame ^= 1; | |
| 228 | } | |
| 229 | ||
| 230 | crt_modulate(&(inst->crt), &(inst->ntsc_settings)); | |
| 231 | crt_demodulate(&(inst->crt), inst->noise); | |
| 232 | if(!inst->progressive) | |
| 233 | { | |
| 234 | inst->field ^= 1; | |
| 235 | } | |
| 236 | } |
| 0 | 0 | #include "frei0r.h" |
| 1 | #include "frei0r_math.h" | |
| 1 | #include "frei0r/math.h" | |
| 2 | 2 | #include <stdlib.h> |
| 3 | 3 | #include <string.h> |
| 4 | 4 | #include <assert.h> |
| 5 | 5 | |
| 6 | 6 | static uint32_t average(const uint32_t* start, |
| 7 | int bxsize, | |
| 8 | int bysize, int xsize); | |
| 7 | int bxsize, | |
| 8 | int bysize, int xsize); | |
| 9 | 9 | |
| 10 | 10 | static void fill_block(uint32_t* start, |
| 11 | 11 | int bxsize, |
| 15 | 15 | |
| 16 | 16 | typedef struct pixelizer_instance |
| 17 | 17 | { |
| 18 | unsigned int width; | |
| 19 | unsigned int height; | |
| 20 | unsigned int block_size_x; | |
| 21 | unsigned int block_size_y; | |
| 18 | unsigned int width; | |
| 19 | unsigned int height; | |
| 20 | unsigned int block_size_x; | |
| 21 | unsigned int block_size_y; | |
| 22 | unsigned char pass_alpha; | |
| 22 | 23 | } pixelizer_instance_t; |
| 23 | 24 | |
| 24 | 25 | int f0r_init() |
| 25 | 26 | { |
| 26 | return 1; | |
| 27 | return 1; | |
| 27 | 28 | } |
| 28 | 29 | |
| 29 | 30 | void f0r_deinit() |
| 31 | 32 | |
| 32 | 33 | void f0r_get_plugin_info(f0r_plugin_info_t* pixelizerInfo) |
| 33 | 34 | { |
| 34 | pixelizerInfo->name = "pixeliz0r"; | |
| 35 | pixelizerInfo->author = "Gephex crew"; | |
| 36 | pixelizerInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 37 | pixelizerInfo->color_model = F0R_COLOR_MODEL_PACKED32; | |
| 38 | pixelizerInfo->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 39 | pixelizerInfo->major_version = 1; | |
| 40 | pixelizerInfo->minor_version = 0; | |
| 41 | pixelizerInfo->num_params = 2; | |
| 42 | pixelizerInfo->explanation = "Pixelize input image."; | |
| 35 | pixelizerInfo->name = "pixeliz0r"; | |
| 36 | pixelizerInfo->author = "Gephex crew"; | |
| 37 | pixelizerInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 38 | pixelizerInfo->color_model = F0R_COLOR_MODEL_PACKED32; | |
| 39 | pixelizerInfo->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 40 | pixelizerInfo->major_version = 2; | |
| 41 | pixelizerInfo->minor_version = 0; | |
| 42 | pixelizerInfo->num_params = 3; | |
| 43 | pixelizerInfo->explanation = "Pixelize input image."; | |
| 43 | 44 | } |
| 44 | 45 | |
| 45 | 46 | void f0r_get_param_info(f0r_param_info_t* info, int param_index) |
| 46 | 47 | { |
| 47 | switch(param_index) | |
| 48 | switch(param_index) | |
| 48 | 49 | { |
| 49 | 50 | case 0: |
| 50 | info->name = "Block width"; | |
| 51 | info->type = F0R_PARAM_DOUBLE; | |
| 52 | info->explanation = "Horizontal size of one 'pixel'"; | |
| 53 | break; | |
| 51 | info->name = "Block width"; | |
| 52 | info->type = F0R_PARAM_DOUBLE; | |
| 53 | info->explanation = "Horizontal size of one 'pixel'"; | |
| 54 | break; | |
| 54 | 55 | case 1: |
| 55 | info->name = "Block height"; | |
| 56 | info->type = F0R_PARAM_DOUBLE; | |
| 57 | info->explanation = "Vertical size of one 'pixel'"; | |
| 58 | break; | |
| 56 | info->name = "Block height"; | |
| 57 | info->type = F0R_PARAM_DOUBLE; | |
| 58 | info->explanation = "Vertical size of one 'pixel'"; | |
| 59 | break; | |
| 60 | case 2: | |
| 61 | info->name = "Pass-through alpha"; | |
| 62 | info->type = F0R_PARAM_BOOL; | |
| 63 | info->explanation = "Do not average the alpha channel over each block"; | |
| 64 | break; | |
| 59 | 65 | } |
| 60 | 66 | } |
| 61 | 67 | |
| 62 | 68 | f0r_instance_t f0r_construct(unsigned int width, unsigned int height) |
| 63 | 69 | { |
| 64 | pixelizer_instance_t* inst = (pixelizer_instance_t*)calloc(1, sizeof(*inst)); | |
| 65 | inst->width = width; inst->height = height; | |
| 66 | inst->block_size_x = 8; inst->block_size_y = 8; | |
| 67 | return (f0r_instance_t)inst; | |
| 70 | pixelizer_instance_t* inst = (pixelizer_instance_t*)calloc(1, sizeof(*inst)); | |
| 71 | inst->width = width; inst->height = height; | |
| 72 | inst->block_size_x = 8; inst->block_size_y = 8; | |
| 73 | return (f0r_instance_t)inst; | |
| 68 | 74 | } |
| 69 | 75 | |
| 70 | 76 | void f0r_destruct(f0r_instance_t instance) |
| 71 | 77 | { |
| 72 | free(instance); | |
| 78 | free(instance); | |
| 73 | 79 | } |
| 74 | 80 | |
| 75 | 81 | void f0r_set_param_value(f0r_instance_t instance, |
| 76 | f0r_param_t param, int param_index) | |
| 82 | f0r_param_t param, int param_index) | |
| 77 | 83 | { |
| 78 | assert(instance); | |
| 79 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 80 | ||
| 81 | switch(param_index) | |
| 84 | assert(instance); | |
| 85 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 86 | ||
| 87 | switch(param_index) | |
| 82 | 88 | { |
| 83 | 89 | case 0: |
| 84 | // scale to [1..width] | |
| 85 | inst->block_size_x = MAX(1 + ( *((double*)param) * (inst->width/2)), 1); | |
| 86 | break; | |
| 90 | // scale to [1..width] | |
| 91 | inst->block_size_x = MAX(1 + ( *((double*)param) * (inst->width/2)), 1); | |
| 92 | break; | |
| 87 | 93 | case 1: |
| 88 | // scale to [1..height] | |
| 89 | inst->block_size_y = MAX(1 + ( *((double*)param) * (inst->height/2)), 1); | |
| 90 | break; | |
| 91 | } | |
| 94 | // scale to [1..height] | |
| 95 | inst->block_size_y = MAX(1 + ( *((double*)param) * (inst->height/2)), 1); | |
| 96 | break; | |
| 97 | case 2: | |
| 98 | inst->pass_alpha = *((double*)param) >= 0.5; | |
| 99 | break; | |
| 100 | } | |
| 92 | 101 | } |
| 93 | 102 | |
| 94 | 103 | void f0r_get_param_value(f0r_instance_t instance, |
| 95 | f0r_param_t param, int param_index) | |
| 104 | f0r_param_t param, int param_index) | |
| 96 | 105 | { |
| 97 | assert(instance); | |
| 98 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 99 | ||
| 100 | switch(param_index) | |
| 106 | assert(instance); | |
| 107 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 108 | ||
| 109 | switch(param_index) | |
| 101 | 110 | { |
| 102 | 111 | case 0: |
| 103 | // scale back to [0..1] | |
| 104 | *((double*)param) = (double)(inst->block_size_x-1)/(inst->width/2); | |
| 105 | break; | |
| 112 | // scale back to [0..1] | |
| 113 | *((double*)param) = (double)(inst->block_size_x-1)/(inst->width/2); | |
| 114 | break; | |
| 106 | 115 | case 1: |
| 107 | // scale back to [0..1] | |
| 108 | *((double*)param) = (double)(inst->block_size_y-1)/(inst->height/2); | |
| 109 | break; | |
| 110 | } | |
| 116 | // scale back to [0..1] | |
| 117 | *((double*)param) = (double)(inst->block_size_y-1)/(inst->height/2); | |
| 118 | break; | |
| 119 | case 2: | |
| 120 | *((double*)param) = inst->pass_alpha ? 1.0 : 0.0; | |
| 121 | break; | |
| 122 | } | |
| 111 | 123 | } |
| 112 | 124 | |
| 113 | 125 | void f0r_update(f0r_instance_t instance, double time, |
| 114 | const uint32_t* inframe, uint32_t* outframe) | |
| 115 | { | |
| 116 | assert(instance); | |
| 117 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 118 | unsigned int xsize = inst->width; | |
| 119 | unsigned int ysize = inst->height; | |
| 120 | unsigned int bsizex = inst->block_size_x; | |
| 121 | unsigned int bsizey = inst->block_size_y; | |
| 122 | unsigned int offset = 0; | |
| 123 | unsigned int blocks_x, blocks_y, xrest, yrest, xi, yi; | |
| 124 | ||
| 125 | ||
| 126 | blocks_x = xsize / bsizex; | |
| 127 | blocks_y = ysize / bsizey; | |
| 128 | xrest = xsize - blocks_x*bsizex; | |
| 129 | yrest = ysize - blocks_y*bsizey; | |
| 130 | ||
| 131 | uint32_t* dst = outframe; | |
| 132 | const uint32_t* src = inframe; | |
| 133 | ||
| 134 | ||
| 135 | if (bsizex == 1 && bsizey == 1) | |
| 136 | { | |
| 137 | memcpy(dst, src, xsize*ysize*sizeof(uint32_t)); | |
| 138 | } | |
| 139 | ||
| 140 | ||
| 141 | // now get average for every block and write the average color to the output | |
| 142 | for (yi = 0; yi < blocks_y; ++yi) | |
| 126 | const uint32_t* inframe, uint32_t* outframe) | |
| 127 | { | |
| 128 | assert(instance); | |
| 129 | pixelizer_instance_t* inst = (pixelizer_instance_t*)instance; | |
| 130 | unsigned int xsize = inst->width; | |
| 131 | unsigned int ysize = inst->height; | |
| 132 | unsigned int bsizex = inst->block_size_x; | |
| 133 | unsigned int bsizey = inst->block_size_y; | |
| 134 | unsigned int offset = 0; | |
| 135 | unsigned int blocks_x, blocks_y, xrest, yrest, xi, yi; | |
| 136 | ||
| 137 | blocks_x = xsize / bsizex; | |
| 138 | blocks_y = ysize / bsizey; | |
| 139 | xrest = xsize - blocks_x*bsizex; | |
| 140 | yrest = ysize - blocks_y*bsizey; | |
| 141 | ||
| 142 | uint32_t* dst = outframe; | |
| 143 | const uint32_t* src = inframe; | |
| 144 | ||
| 145 | ||
| 146 | if (bsizex == 1 && bsizey == 1) | |
| 147 | { | |
| 148 | memcpy(dst, src, xsize*ysize*sizeof(uint32_t)); | |
| 149 | } | |
| 150 | ||
| 151 | ||
| 152 | // now get average for every block and write the average color to the output | |
| 153 | for (yi = 0; yi < blocks_y; ++yi) | |
| 143 | 154 | { |
| 144 | offset = yi*bsizey*xsize; | |
| 145 | for (xi = 0; xi < blocks_x; ++xi) | |
| 146 | { | |
| 147 | uint32_t col = average(src + offset, bsizex, bsizey, xsize); | |
| 148 | fill_block(dst + offset, bsizex, bsizey, xsize, col); | |
| 149 | offset += bsizex; | |
| 150 | } | |
| 151 | if (xrest > 0) | |
| 152 | { | |
| 153 | uint32_t col = average(src + offset, xrest, bsizey, xsize); | |
| 154 | fill_block(dst + offset, xrest, bsizey, xsize, col); | |
| 155 | } | |
| 156 | } | |
| 157 | // check for last line | |
| 158 | if (yrest > 0) | |
| 159 | { | |
| 160 | offset = blocks_y*bsizey*xsize; | |
| 161 | for (xi = 0; xi < blocks_x; ++xi) | |
| 162 | { | |
| 163 | uint32_t col = average(src + offset, bsizex, yrest, xsize); | |
| 164 | fill_block(dst + offset, bsizex, yrest, xsize, col); | |
| 165 | offset += bsizex; | |
| 166 | } | |
| 167 | if (xrest > 0) | |
| 168 | { | |
| 169 | uint32_t col = average(src + offset, xrest, yrest, xsize); | |
| 170 | fill_block(dst + offset, xrest, yrest, xsize, col); | |
| 171 | } | |
| 172 | } | |
| 155 | offset = yi*bsizey*xsize; | |
| 156 | for (xi = 0; xi < blocks_x; ++xi) | |
| 157 | { | |
| 158 | uint32_t col = average(src + offset, bsizex, bsizey, xsize); | |
| 159 | fill_block(dst + offset, bsizex, bsizey, xsize, col); | |
| 160 | offset += bsizex; | |
| 161 | } | |
| 162 | if (xrest > 0) | |
| 163 | { | |
| 164 | uint32_t col = average(src + offset, xrest, bsizey, xsize); | |
| 165 | fill_block(dst + offset, xrest, bsizey, xsize, col); | |
| 166 | } | |
| 167 | } | |
| 168 | // check for last line | |
| 169 | if (yrest > 0) | |
| 170 | { | |
| 171 | offset = blocks_y*bsizey*xsize; | |
| 172 | for (xi = 0; xi < blocks_x; ++xi) | |
| 173 | { | |
| 174 | uint32_t col = average(src + offset, bsizex, yrest, xsize); | |
| 175 | fill_block(dst + offset, bsizex, yrest, xsize, col); | |
| 176 | offset += bsizex; | |
| 177 | } | |
| 178 | if (xrest > 0) | |
| 179 | { | |
| 180 | uint32_t col = average(src + offset, xrest, yrest, xsize); | |
| 181 | fill_block(dst + offset, xrest, yrest, xsize, col); | |
| 182 | } | |
| 183 | } | |
| 184 | if (inst->pass_alpha) | |
| 185 | { | |
| 186 | for (xi = 0; xi < inst->width * inst->height; ++xi) | |
| 187 | { | |
| 188 | dst[xi] = (src[xi] & 0xff000000) | (dst[xi] & 0x00ffffff); | |
| 189 | } | |
| 190 | } | |
| 173 | 191 | } |
| 174 | 192 | |
| 175 | 193 | |
| 176 | 194 | static uint32_t average(const uint32_t* start, |
| 177 | int bxsize, int bysize, int xsize) | |
| 178 | { | |
| 179 | const uint32_t* p = start; | |
| 180 | uint32_t alpha = 0, red = 0, green = 0, blue = 0; | |
| 181 | uint32_t avg_alpha, avg_red, avg_green, avg_blue; | |
| 182 | int x, y; | |
| 183 | const uint32_t* pp; | |
| 184 | uint32_t c; | |
| 185 | ||
| 186 | for (y = 0; y < bysize; ++y) | |
| 187 | { | |
| 188 | pp = p; | |
| 189 | for (x = 0; x < bxsize; ++x) | |
| 190 | { | |
| 191 | c = *(pp++); | |
| 192 | alpha += (c & 0xff000000) >> 24; | |
| 193 | red += (c & 0x00ff0000) >> 16; | |
| 194 | green += (c & 0x0000ff00) >> 8; | |
| 195 | blue += (c & 0x000000ff); | |
| 196 | } | |
| 197 | p += xsize; | |
| 198 | } | |
| 199 | ||
| 200 | avg_alpha = (alpha / (bxsize*bysize)) & 0xff; | |
| 201 | avg_red = (red / (bxsize*bysize)) & 0xff; | |
| 202 | avg_green = (green / (bxsize*bysize)) & 0xff; | |
| 203 | avg_blue = (blue / (bxsize*bysize)) & 0xff; | |
| 204 | ||
| 205 | return (avg_alpha << 24) + (avg_red << 16) + (avg_green << 8) + avg_blue; | |
| 195 | int bxsize, int bysize, int xsize) | |
| 196 | { | |
| 197 | const uint32_t* p = start; | |
| 198 | uint32_t alpha = 0, red = 0, green = 0, blue = 0; | |
| 199 | uint32_t avg_alpha, avg_red, avg_green, avg_blue; | |
| 200 | int x, y; | |
| 201 | const uint32_t* pp; | |
| 202 | uint32_t c; | |
| 203 | ||
| 204 | for (y = 0; y < bysize; ++y) | |
| 205 | { | |
| 206 | pp = p; | |
| 207 | for (x = 0; x < bxsize; ++x) | |
| 208 | { | |
| 209 | c = *(pp++); | |
| 210 | alpha += (c & 0xff000000) >> 24; | |
| 211 | red += (c & 0x00ff0000) >> 16; | |
| 212 | green += (c & 0x0000ff00) >> 8; | |
| 213 | blue += (c & 0x000000ff); | |
| 214 | } | |
| 215 | p += xsize; | |
| 216 | } | |
| 217 | ||
| 218 | avg_alpha = (alpha / (bxsize*bysize)) & 0xff; | |
| 219 | avg_red = (red / (bxsize*bysize)) & 0xff; | |
| 220 | avg_green = (green / (bxsize*bysize)) & 0xff; | |
| 221 | avg_blue = (blue / (bxsize*bysize)) & 0xff; | |
| 222 | ||
| 223 | return (avg_alpha << 24) + (avg_red << 16) + (avg_green << 8) + avg_blue; | |
| 206 | 224 | } |
| 207 | 225 | |
| 208 | 226 | static void fill_block(uint32_t* start, int bxsize, int bysize, |
| 209 | 227 | int xsize, uint32_t col) |
| 210 | 228 | { |
| 211 | uint32_t* p = start; | |
| 212 | int x, y; | |
| 213 | uint32_t* pp; | |
| 214 | ||
| 215 | for (y = 0; y < bysize; ++y) | |
| 216 | { | |
| 217 | pp = p; | |
| 218 | for (x = 0; x < bxsize; ++x) | |
| 219 | { | |
| 220 | *(pp++) = col; | |
| 221 | } | |
| 222 | p += xsize; | |
| 223 | } | |
| 224 | ||
| 225 | } | |
| 226 | ||
| 229 | uint32_t* p = start; | |
| 230 | int x, y; | |
| 231 | uint32_t* pp; | |
| 232 | ||
| 233 | for (y = 0; y < bysize; ++y) | |
| 234 | { | |
| 235 | pp = p; | |
| 236 | for (x = 0; x < bxsize; ++x) | |
| 237 | { | |
| 238 | *(pp++) = col; | |
| 239 | } | |
| 240 | p += xsize; | |
| 241 | } | |
| 242 | ||
| 243 | } | |
| 244 | ||
| 25 | 25 | #include <assert.h> |
| 26 | 26 | |
| 27 | 27 | #include "frei0r.h" |
| 28 | #include "frei0r_math.h" | |
| 28 | #include "frei0r/math.h" | |
| 29 | 29 | |
| 30 | 30 | typedef struct posterize_instance |
| 31 | 31 | { |
| 16 | 16 | */ |
| 17 | 17 | |
| 18 | 18 | #include "frei0r.hpp" |
| 19 | #include "frei0r_math.h" | |
| 19 | #include "frei0r/math.h" | |
| 20 | 20 | |
| 21 | 21 | class Premultiply : public frei0r::filter |
| 22 | 22 | { |
| 26 | 26 | #include <assert.h> |
| 27 | 27 | #include <math.h> |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_math.h" | |
| 29 | #include "frei0r/math.h" | |
| 30 | 30 | |
| 31 | 31 | static int MY_MAX_RAND = 32767;// I assume RAND_MAX to be at least this big. |
| 32 | 32 | static double gaussian_lookup[32767]; |
| 20 | 20 | #include <assert.h> |
| 21 | 21 | |
| 22 | 22 | #include "frei0r.h" |
| 23 | #include "frei0r_math.h" | |
| 23 | #include "frei0r/math.h" | |
| 24 | 24 | |
| 25 | 25 | #define MAX_SATURATION 8.0 |
| 26 | 26 |
| 25 | 25 | #include <stdio.h> |
| 26 | 26 | #include <math.h> |
| 27 | 27 | #include "frei0r.h" |
| 28 | #include "frei0r_math.h" | |
| 28 | #include "frei0r/math.h" | |
| 29 | 29 | |
| 30 | 30 | #define SIGMOIDAL_BASE 2 |
| 31 | 31 | #define SIGMOIDAL_RANGE 20 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | #include <stdlib.h> |
| 22 | 22 | |
| 23 | 23 | class sobel : public frei0r::filter |
| 26 | 26 | #include <assert.h> |
| 27 | 27 | #include <math.h> |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "blur.h" | |
| 30 | #include "frei0r_math.h" | |
| 29 | #include "frei0r/blur.h" | |
| 30 | #include "frei0r/math.h" | |
| 31 | 31 | |
| 32 | 32 | #define SIGMOIDAL_BASE 2 |
| 33 | 33 | #define SIGMOIDAL_RANGE 20 |
| 19 | 19 | #include <math.h> |
| 20 | 20 | #include <stdlib.h> |
| 21 | 21 | #include "frei0r.hpp" |
| 22 | #include "frei0r_math.h" | |
| 22 | #include "frei0r/math.h" | |
| 23 | 23 | |
| 24 | 24 | /** |
| 25 | 25 | This filter implements a standard way of color correction proposed by |
| 23 | 23 | #include <string.h> |
| 24 | 24 | |
| 25 | 25 | #include "frei0r.h" |
| 26 | #include "blur.h" | |
| 26 | #include "frei0r/blur.h" | |
| 27 | 27 | |
| 28 | 28 | int f0r_init() |
| 29 | 29 | { |
| 0 | 0 | # Set C99 flag for gcc |
| 1 | 1 | if (CMAKE_COMPILER_IS_GNUCC) |
| 2 | set(CMAKE_C_FLAGS "-std=c99") | |
| 2 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") | |
| 3 | 3 | endif (CMAKE_COMPILER_IS_GNUCC) |
| 4 | 4 | |
| 5 | 5 | set (SOURCES three_point_balance.c) |
| 23 | 23 | #include <stdio.h> |
| 24 | 24 | |
| 25 | 25 | #include "frei0r.h" |
| 26 | #include "frei0r_math.h" | |
| 26 | #include "frei0r/math.h" | |
| 27 | 27 | |
| 28 | 28 | typedef struct three_point_balance_instance |
| 29 | 29 | { |
| 20 | 20 | #include <assert.h> |
| 21 | 21 | |
| 22 | 22 | #include "frei0r.h" |
| 23 | #include "frei0r_math.h" | |
| 23 | #include "frei0r/math.h" | |
| 24 | 24 | |
| 25 | 25 | typedef struct tint0r_instance |
| 26 | 26 | { |
| 0 | 0 | #include "frei0r.h" |
| 1 | #include "frei0r_math.h" | |
| 1 | #include "frei0r/math.h" | |
| 2 | 2 | #include <stdlib.h> |
| 3 | 3 | #include <assert.h> |
| 4 | 4 |
| 21 | 21 | #endif /* _MSC_VER */ |
| 22 | 22 | #include <cmath> |
| 23 | 23 | #include "frei0r.hpp" |
| 24 | #include "frei0r_math.h" | |
| 24 | #include "frei0r/math.h" | |
| 25 | 25 | |
| 26 | 26 | |
| 27 | 27 | /** |
| 18 | 18 | add_subdirectory (difference) |
| 19 | 19 | add_subdirectory (divide) |
| 20 | 20 | add_subdirectory (dodge) |
| 21 | add_subdirectory (euclid_eraser) | |
| 21 | 22 | add_subdirectory (grain_extract) |
| 22 | 23 | add_subdirectory (grain_merge) |
| 23 | 24 | add_subdirectory (hardlight) |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 19 | 19 | */ |
| 20 | 20 | |
| 21 | 21 | #include "frei0r.hpp" |
| 22 | #include "frei0r_math.h" | |
| 22 | #include "frei0r/math.h" | |
| 23 | 23 | |
| 24 | 24 | #define NBYTES 4 |
| 25 | 25 | #define ALPHA 3 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | |
| 22 | 22 | #include <algorithm> |
| 23 | 23 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | |
| 22 | 22 | #include <algorithm> |
| 23 | 23 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | |
| 22 | 22 | #include <algorithm> |
| 23 | 23 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | |
| 22 | 22 | #include <algorithm> |
| 23 | 23 |
| 17 | 17 | */ |
| 18 | 18 | |
| 19 | 19 | #include "frei0r.hpp" |
| 20 | #include "frei0r_math.h" | |
| 20 | #include "frei0r/math.h" | |
| 21 | 21 | |
| 22 | 22 | #include <algorithm> |
| 23 | 23 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 26 | 26 | #include <math.h> |
| 27 | 27 | |
| 28 | 28 | #include "frei0r.h" |
| 29 | #include "frei0r_cairo.h" | |
| 29 | #include "frei0r/cairo.h" | |
| 30 | 30 | |
| 31 | 31 | double PI=3.14159265358979; |
| 32 | 32 |
| 25 | 25 | #include <string.h> |
| 26 | 26 | |
| 27 | 27 | #include "frei0r.h" |
| 28 | #include "frei0r_cairo.h" | |
| 28 | #include "frei0r/cairo.h" | |
| 29 | 29 | |
| 30 | 30 | typedef struct cairo_blend_instance |
| 31 | 31 | { |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 22 | #include "frei0r_colorspace.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | #include "frei0r/colorspace.h" | |
| 23 | 23 | |
| 24 | 24 | #define NBYTES 4 |
| 25 | 25 |
| 20 | 20 | #include <assert.h> |
| 21 | 21 | |
| 22 | 22 | #include "frei0r.h" |
| 23 | #include "frei0r_math.h" | |
| 23 | #include "frei0r/math.h" | |
| 24 | 24 | |
| 25 | 25 | typedef struct composition_instance |
| 26 | 26 | { |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 0 | set (SOURCES euclid_eraser.cpp) | |
| 1 | set (TARGET euclid_eraser) | |
| 2 | ||
| 3 | if (MSVC) | |
| 4 | set (SOURCES ${SOURCES} ${FREI0R_1_1_DEF}) | |
| 5 | endif (MSVC) | |
| 6 | ||
| 7 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 8 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 9 | ||
| 10 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) |
| 0 | // SPDX-License-Identifier: GPL-2.0-or-later | |
| 1 | // Copyright (C) 2024 Erik H. Beck, bacon@tahomasoft.com | |
| 2 | // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html | |
| 3 | ||
| 4 | /* This program is free software; you can redistribute it and/or modify | |
| 5 | * it under the terms of the GNU General Public License as published by | |
| 6 | * the Free Software Foundation; either version 2 of the License, or | |
| 7 | * (at your option) any later version. | |
| 8 | * | |
| 9 | * This program is distributed in the hope that it will be useful, | |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 12 | * GNU General Public License for more details. | |
| 13 | * | |
| 14 | * You should have received a copy of the GNU General Public License | |
| 15 | * along with this program; if not, write to the Free Software | |
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 17 | * 02110-1301, USA | |
| 18 | * | |
| 19 | * Also see: | |
| 20 | * | |
| 21 | * https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html | |
| 22 | * | |
| 23 | */ | |
| 24 | ||
| 25 | /** This is the source file for the euclid eraser two-input mixer for | |
| 26 | frei0r. It uses euclidean distance to remove a static background | |
| 27 | from a video. See file ./euclid_eraser.md for more info | |
| 28 | */ | |
| 29 | ||
| 30 | #include <math.h> | |
| 31 | #include "frei0r.hpp" | |
| 32 | #include "frei0r/math.h" | |
| 33 | ||
| 34 | #define NBYTES 4 | |
| 35 | #define CHANNELS 3 // Actually 4; 0-3 | |
| 36 | ||
| 37 | double euclidDistance(uint8_t x_r, uint8_t x_g, uint8_t x_b, | |
| 38 | uint8_t y_r, uint8_t y_g, uint8_t y_b) | |
| 39 | { | |
| 40 | //calculating color channel differences for next steps | |
| 41 | double red_d = x_r - y_r; | |
| 42 | double green_d = x_g - y_g; | |
| 43 | double blue_d = x_b - y_b; | |
| 44 | ||
| 45 | double sq_sum, dist; | |
| 46 | ||
| 47 | //calculating Euclidean distance | |
| 48 | sq_sum = pow(red_d, 2) + pow(green_d, 2) + pow (blue_d, 2); | |
| 49 | dist = sqrt(sq_sum); | |
| 50 | ||
| 51 | return dist; | |
| 52 | } | |
| 53 | ||
| 54 | ||
| 55 | class euclid_eraser : public frei0r::mixer2 | |
| 56 | { | |
| 57 | ||
| 58 | public: | |
| 59 | euclid_eraser(unsigned int width, unsigned int height) | |
| 60 | { | |
| 61 | threshold = 5.6; // Default distance threshold value | |
| 62 | register_param(threshold, "threshold", "Matching Threshold"); | |
| 63 | } | |
| 64 | ||
| 65 | void update(double time, | |
| 66 | uint32_t* out, | |
| 67 | const uint32_t* in1, | |
| 68 | const uint32_t* in2) | |
| 69 | { | |
| 70 | ||
| 71 | const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1); //frst track (0) | |
| 72 | const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2); //second trk (1) | |
| 73 | uint8_t *dst = reinterpret_cast<uint8_t*>(out); | |
| 74 | ||
| 75 | double e_dist; | |
| 76 | ||
| 77 | uint32_t sizeCounter = size; | |
| 78 | uint32_t b; | |
| 79 | ||
| 80 | while (sizeCounter--) | |
| 81 | { | |
| 82 | ||
| 83 | // Loop over rgb | |
| 84 | // Copy pixels from src2 to destination | |
| 85 | ||
| 86 | for (b=0; b<3; b++) | |
| 87 | { | |
| 88 | dst[b]=src2[b]; | |
| 89 | } | |
| 90 | e_dist=euclidDistance(src1[0],src1[1],src1[2], | |
| 91 | src2[0],src2[1],src2[2]); | |
| 92 | ||
| 93 | if (e_dist <= euclid_eraser::threshold) { | |
| 94 | // Make alpha channel for pixel fully transparent | |
| 95 | dst[3]=0; | |
| 96 | } | |
| 97 | else { | |
| 98 | // Make alpha channel for the pixel fully opaque | |
| 99 | dst[3]=255; | |
| 100 | } | |
| 101 | ||
| 102 | src1 += NBYTES; | |
| 103 | src2 += NBYTES; | |
| 104 | dst += NBYTES; | |
| 105 | } | |
| 106 | ||
| 107 | } | |
| 108 | private: | |
| 109 | double threshold; | |
| 110 | ||
| 111 | }; | |
| 112 | ||
| 113 | frei0r::construct<euclid_eraser> plugin("euclid_eraser", | |
| 114 | "Erasing backgrounds with euclidian distance", | |
| 115 | "Erik H. Beck", | |
| 116 | 0,1, | |
| 117 | F0R_COLOR_MODEL_RGBA8888); | |
| 118 |
| 0 | # Euclid Eraser Notes # | |
| 1 | ||
| 2 | ## Overview ## | |
| 3 | This is about the Euclid Eraser mixer. | |
| 4 | ||
| 5 | ||
| 6 | ## Description ## | |
| 7 | ||
| 8 | This class is intended to operate as a mixer, taking two inputs and | |
| 9 | yielding one output. | |
| 10 | ||
| 11 | The first input is a reference input, such as a single image or a | |
| 12 | stretched video of a single image. | |
| 13 | ||
| 14 | The second input is the video stream to operate on. | |
| 15 | ||
| 16 | The output is a clone of the RGB data of the second input, but with | |
| 17 | the alpha channel modified. | |
| 18 | ||
| 19 | This mixer takes the (first) reference input, such as a static | |
| 20 | background, and removes it from every frame in the video stream of the | |
| 21 | second input. | |
| 22 | ||
| 23 | The alpha channel on the output is based on the euclidian distance of | |
| 24 | the two input coordinates in 3-d RGB space. If the calculated distance | |
| 25 | betwen the two inputs for a given pixel is less than a provided | |
| 26 | (variable) threshold amount, that indicates the pixel in the | |
| 27 | background (reference) image is the same or similar enough to the | |
| 28 | operational (second) input that is part of the background to be | |
| 29 | removed, and the transparency is set to fully transparent via the | |
| 30 | alpha channel (set to 0). | |
| 31 | ||
| 32 | If the calcuated distance exceeds the threshold, then that pixel is | |
| 33 | part of the foreground image to be retained, and the transparency | |
| 34 | of it is set to be fully opaque (alpha channel for that pixel set | |
| 35 | to 255). | |
| 36 | ||
| 37 | ## Basic Algorithm ## | |
| 38 | The basic comparison algorithm is: | |
| 39 | ||
| 40 | ||
| 41 | x is reference image | |
| 42 | y is comparison image to remove reference image from | |
| 43 | ||
| 44 | ||
| 45 | ``` | |
| 46 | float euclidDistance (int x_r, int x_g, int x_b, int y_r, int y_g, int y_b) | |
| 47 | { | |
| 48 | //calculating color channel differences for next steps | |
| 49 | float red_d = x_r - y_r; | |
| 50 | float green_d = x_g - y_g; | |
| 51 | float blue_d = x_b - y_b; | |
| 52 | ||
| 53 | float sq_sum, dist; | |
| 54 | ||
| 55 | //calculating Euclidean distance | |
| 56 | sq_sum = pow(red_d, 2) + pow(green_d, 2) + pow (blue_d) | |
| 57 | dist = sqrt(sq_sum); | |
| 58 | ||
| 59 | return dist; | |
| 60 | } | |
| 61 | ``` | |
| 62 | ||
| 63 | ## Note ## | |
| 64 | ||
| 65 | Here's a handy reminder on how the bits map up. | |
| 66 | ||
| 67 | ``` | |
| 68 | red_src1 = src1[0]; | |
| 69 | green_src1 = src1[1]; | |
| 70 | blue_src1 = src1[2]; | |
| 71 | alpha_src1 = src1[3] | |
| 72 | ||
| 73 | red_src2 = src2[0]; | |
| 74 | green_src2 = src2[1]; | |
| 75 | blue_src2 = src2[2]; | |
| 76 | alpha_src2 = src2[3] | |
| 77 | ``` | |
| 78 | ## Further Work ## | |
| 79 | ||
| 80 | Some potential improvements are: | |
| 81 | ||
| 82 | - Faster performance (math calculations, others) | |
| 83 | - Options beyond binary for alpha | |
| 84 | ||
| 85 | ||
| 86 | ||
| 87 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 22 | #include "frei0r_colorspace.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | #include "frei0r/colorspace.h" | |
| 23 | 23 | |
| 24 | 24 | #define NBYTES 4 |
| 25 | 25 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 22 | #include "frei0r_colorspace.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | #include "frei0r/colorspace.h" | |
| 23 | 23 | |
| 24 | 24 | #define NBYTES 4 |
| 25 | 25 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |
| 18 | 18 | */ |
| 19 | 19 | |
| 20 | 20 | #include "frei0r.hpp" |
| 21 | #include "frei0r_math.h" | |
| 21 | #include "frei0r/math.h" | |
| 22 | 22 | |
| 23 | 23 | #define NBYTES 4 |
| 24 | 24 | #define ALPHA 3 |