Update upstream source from tag 'upstream/2.5.1'
Update to upstream version '2.5.1'
with Debian dir 2adb8d4505405a73d5f43a8785e5117ab4d2c56e
IOhannes m zmölnig (Debian/GNU)
6 months ago
| 9 | 9 | |
| 10 | 10 | + [Cairo](http://cairographics.org) required for cairo- filters and mixers |
| 11 | 11 | |
| 12 | ## Optional build flags | |
| 13 | ||
| 14 | + `-DWITHOUT_FACERECOGNITION=ON` - Disable face recognition plugins (facedetect and facebl0r) to avoid protobuf conflicts with applications like MLT | |
| 15 | ||
| 12 | 16 | It is recommended to use a separate `build` sub-folder. |
| 13 | 17 | |
| 14 | 18 | ``` |
| 15 | 19 | mkdir -p build |
| 16 | 20 | cd build && cmake ../ |
| 21 | make | |
| 22 | ``` | |
| 23 | ||
| 24 | To disable face recognition plugins (recommended when using with MLT): | |
| 25 | ``` | |
| 26 | mkdir -p build | |
| 27 | cd build && cmake -DWITHOUT_FACERECOGNITION=ON ../ | |
| 17 | 28 | make |
| 18 | 29 | ``` |
| 19 | 30 |
| 0 | cmake_minimum_required (VERSION 3.12...3.31) | |
| 0 | cmake_minimum_required (VERSION 3.12) | |
| 1 | 1 | |
| 2 | 2 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) |
| 3 | 3 | |
| 4 | 4 | project (frei0r) |
| 5 | set (VERSION 1.8) | |
| 5 | set (VERSION 2.5.1) | |
| 6 | 6 | |
| 7 | 7 | include(GNUInstallDirs) |
| 8 | 8 | |
| 9 | 9 | option (WITHOUT_OPENCV "Disable plugins dependent upon OpenCV" OFF) |
| 10 | option (WITHOUT_FACERECOGNITION "Disable facedetect plugin to avoid protobuf conflicts" OFF) | |
| 11 | ||
| 10 | 12 | if (NOT WITHOUT_OPENCV) |
| 11 | 13 | find_package (OpenCV) |
| 12 | 14 | endif () |
| 51 | 51 | ## https://github.com/dyne/frei0r/releases |
| 52 | 52 | |
| 53 | 53 | Frei0r sourcecode is released under the terms of the GNU General Public License and, eventually other compatible Free Software licenses. |
| 54 | ||
| 55 | ## Packaging | |
| 56 | ||
| 57 | [](https://repology.org/project/frei0r/versions) | |
| 54 | 58 | |
| 55 | 59 | ## Build dependencies |
| 56 | 60 |
| 61 | 61 | */ |
| 62 | 62 | void frei0r_cairo_set_operator(cairo_t *cr, char *op) |
| 63 | 63 | { |
| 64 | // Validate inputs | |
| 65 | if (!cr || !op) { | |
| 66 | if (cr) { | |
| 67 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); | |
| 68 | } | |
| 69 | return; | |
| 70 | } | |
| 71 | ||
| 64 | 72 | if(strcmp(op, NORMAL) == 0) |
| 65 | 73 | { |
| 66 | 74 | cairo_set_operator (cr, CAIRO_OPERATOR_OVER); |
| 230 | 238 | */ |
| 231 | 239 | void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) |
| 232 | 240 | { |
| 241 | // Validate inputs | |
| 242 | if (!rgba || pixels <= 0) { | |
| 243 | return; | |
| 244 | } | |
| 245 | ||
| 233 | 246 | int i = pixels + 1; |
| 234 | 247 | while ( --i ) { |
| 235 | 248 | register unsigned char a = rgba[3]; |
| 254 | 267 | */ |
| 255 | 268 | void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) |
| 256 | 269 | { |
| 270 | // Validate inputs | |
| 271 | if (!rgba || pixels <= 0) { | |
| 272 | return; | |
| 273 | } | |
| 274 | ||
| 257 | 275 | int i = pixels + 1; |
| 258 | 276 | while ( --i ) { |
| 259 | 277 | register unsigned char a = rgba[3]; |
| 280 | 298 | void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, |
| 281 | 299 | int pixels, int alpha) |
| 282 | 300 | { |
| 301 | // Validate inputs | |
| 302 | if (!in || !out || pixels <= 0) { | |
| 303 | return; | |
| 304 | } | |
| 305 | ||
| 283 | 306 | int i = pixels + 1; |
| 284 | 307 | while ( --i ) { |
| 285 | 308 | register unsigned char a = in[3]; |
| 17 | 17 | add_subdirectory (3dflippo) |
| 18 | 18 | add_subdirectory (aech0r) |
| 19 | 19 | add_subdirectory (alpha0ps) |
| 20 | add_subdirectory (autothresh0ld) | |
| 20 | 21 | add_subdirectory (balanc0r) |
| 21 | 22 | add_subdirectory (baltan) |
| 22 | 23 | add_subdirectory (bluescreen0r) |
| 0 | set (SOURCES autothresh0ld.c) | |
| 1 | set (TARGET autothresh0ld) | |
| 2 | ||
| 3 | if (MSVC) | |
| 4 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 5 | endif (MSVC) | |
| 6 | ||
| 7 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 8 | ||
| 9 | # No «lib» prefix (name.so instead of libname.so) | |
| 10 | set_target_properties (${TARGET} PROPERTIES PREFIX "") |
| 0 | /** | |
| 1 | * (c) Copyright 2025 Cynthia <cynthia2048@proton.me> | |
| 2 | * | |
| 3 | * This is based on Otsu's algorithm to segment an image into foreground | |
| 4 | * or background based on the shape of the histogram. Instead of doing a | |
| 5 | * hard threshold, we use the threshold obtained from Otsu's algorithm | |
| 6 | * as the base for a sigmoidal transfer with a high slope; this produces | |
| 7 | * a more eye-soothing threshold effect as seen in ImageMagick. | |
| 8 | * | |
| 9 | * This has the added benefit that, whereas the sigmoidal filter's base | |
| 10 | * requires manual tuning, here it is determined algorithmically and thus | |
| 11 | * can adapt on a frame-to-frame basis. | |
| 12 | * | |
| 13 | * This file is a Frei0r plugin. | |
| 14 | * | |
| 15 | * This program is free software; you can redistribute it and/or modify | |
| 16 | * it under the terms of the GNU General Public License as published by | |
| 17 | * the Free Software Foundation; either version 2 of the License, or | |
| 18 | * (at your option) any later version. | |
| 19 | * | |
| 20 | * This program is distributed in the hope that it will be useful, | |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 23 | * GNU General Public License for more details. | |
| 24 | * | |
| 25 | * You should have received a copy of the GNU General Public License | |
| 26 | * along with this program; if not, write to the Free Software | |
| 27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 28 | */ | |
| 29 | ||
| 30 | #include <stdint.h> | |
| 31 | #include <stdlib.h> | |
| 32 | #include <math.h> | |
| 33 | ||
| 34 | #include "frei0r.h" | |
| 35 | #include "frei0r/math.h" | |
| 36 | ||
| 37 | #include "variance.h" | |
| 38 | ||
| 39 | typedef struct { | |
| 40 | unsigned int width, height; | |
| 41 | float slope; | |
| 42 | ||
| 43 | uint8_t lut[256][256]; | |
| 44 | uint8_t *lumaframe; | |
| 45 | } s0ft0tsu_t; | |
| 46 | ||
| 47 | static void gen_sigmoid_lut (uint8_t lut[256][256], float slope) | |
| 48 | { | |
| 49 | float k = expf(slope) / 255.0; | |
| 50 | ||
| 51 | for (int j = 0; j < 256; ++j) | |
| 52 | for (int i = 0; i < 256; ++i) | |
| 53 | lut[j][i] = CLAMP (255.0 / (1.0 + expf(-k * (i - j))), 0, 255); | |
| 54 | } | |
| 55 | ||
| 56 | int f0r_init() | |
| 57 | { | |
| 58 | return 0; | |
| 59 | } | |
| 60 | ||
| 61 | void f0r_deinit() {} | |
| 62 | ||
| 63 | f0r_instance_t f0r_construct(unsigned int width, unsigned int height) | |
| 64 | { | |
| 65 | s0ft0tsu_t* s = calloc(1, sizeof(s0ft0tsu_t)); | |
| 66 | ||
| 67 | s->width = width; | |
| 68 | s->height = height; | |
| 69 | s->slope = 4.0; | |
| 70 | s->lumaframe = malloc(s->width * s->height); | |
| 71 | ||
| 72 | gen_sigmoid_lut(s->lut, s->slope); | |
| 73 | ||
| 74 | return s; | |
| 75 | } | |
| 76 | ||
| 77 | void f0r_get_plugin_info(f0r_plugin_info_t* info) | |
| 78 | { | |
| 79 | info->name = "autothreshold"; | |
| 80 | info->author = "Cynthia"; | |
| 81 | info->explanation = "Automatically threshold moving pictures"; | |
| 82 | info->major_version = 0; | |
| 83 | info->minor_version = 1; | |
| 84 | info->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 85 | info->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 86 | info->color_model = F0R_COLOR_MODEL_RGBA8888; | |
| 87 | info->num_params = 1; | |
| 88 | } | |
| 89 | ||
| 90 | void f0r_get_param_info(f0r_param_info_t *info, int index) | |
| 91 | { | |
| 92 | switch (index) | |
| 93 | { | |
| 94 | case 0: | |
| 95 | info->name = "Slope"; | |
| 96 | info->explanation = "Slope of sigmoidal transfer"; | |
| 97 | info->type = F0R_PARAM_DOUBLE; | |
| 98 | break; | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | void f0r_get_param_value(f0r_instance_t inst, f0r_param_t param, int index) | |
| 103 | { | |
| 104 | s0ft0tsu_t* s = inst; | |
| 105 | ||
| 106 | switch (index) | |
| 107 | { | |
| 108 | case 0: | |
| 109 | *(double*)param = s->slope / 7.0; | |
| 110 | break; | |
| 111 | } | |
| 112 | } | |
| 113 | ||
| 114 | void f0r_set_param_value(f0r_instance_t inst, f0r_param_t param, int index) | |
| 115 | { | |
| 116 | s0ft0tsu_t* s = inst; | |
| 117 | ||
| 118 | switch (index) | |
| 119 | { | |
| 120 | case 0: | |
| 121 | s->slope = *(double*)param * 7.0; | |
| 122 | ||
| 123 | gen_sigmoid_lut(s->lut, s->slope); | |
| 124 | break; | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | void f0r_update(f0r_instance_t inst, double time, | |
| 129 | const uint32_t* inframe, uint32_t* outframe) | |
| 130 | { | |
| 131 | s0ft0tsu_t *s = inst; | |
| 132 | ||
| 133 | uint8_t *src = (uint8_t*)inframe; | |
| 134 | uint8_t *dst = s->lumaframe; | |
| 135 | uint8_t r, g, b, luma; | |
| 136 | size_t len = s->width * s->height; | |
| 137 | ||
| 138 | // Normalised histogram (L = 256) | |
| 139 | float hist[256] = {0}; | |
| 140 | ||
| 141 | for (int i = 0; i < len; ++i) | |
| 142 | { | |
| 143 | r = *src++; | |
| 144 | g = *src++; | |
| 145 | b = *src++; | |
| 146 | src++; // Ignore alpha | |
| 147 | ||
| 148 | luma = CLAMP(0.299 * r + 0.587 * g + 0.114 * b, 0, 255); | |
| 149 | *dst++ = luma; | |
| 150 | ||
| 151 | ++hist[luma]; // Add to histogram | |
| 152 | } | |
| 153 | ||
| 154 | // Normalise histogram; becomes a probability distribution | |
| 155 | for (int i = 0; i < 256; ++i) | |
| 156 | hist[i] /= len; | |
| 157 | ||
| 158 | uint8_t thresh = 0.0; | |
| 159 | float max_var = 0.0; // Maximum inter-class variance. | |
| 160 | ||
| 161 | for (int i = 1; i < 256; ++i) | |
| 162 | { | |
| 163 | float var = find_variance(hist, i); | |
| 164 | if (var > max_var) | |
| 165 | { | |
| 166 | thresh = i; | |
| 167 | max_var = var; | |
| 168 | } | |
| 169 | } | |
| 170 | ||
| 171 | src = (uint8_t*)s->lumaframe; // Replenish | |
| 172 | dst = (uint8_t*)outframe; | |
| 173 | ||
| 174 | for (int i = 0; i < len; ++i) | |
| 175 | { | |
| 176 | // Select the appropriate transfer | |
| 177 | uint8_t* lut = s->lut[thresh]; | |
| 178 | ||
| 179 | luma = lut[*src++]; | |
| 180 | ||
| 181 | *dst++ = luma; | |
| 182 | *dst++ = luma; | |
| 183 | *dst++ = luma; | |
| 184 | *dst++ = 0xFF; // TODO Copy alpha | |
| 185 | } | |
| 186 | } | |
| 187 | ||
| 188 | void f0r_destruct(f0r_instance_t s) | |
| 189 | { | |
| 190 | free(s); | |
| 191 | } |
| 0 | #ifdef __SSE2__ | |
| 1 | #include <emmintrin.h> | |
| 2 | #endif | |
| 3 | ||
| 4 | static float find_variance(float *hist, int thresh) | |
| 5 | { | |
| 6 | float w_0 = 0.0, mu_0 = 0.0, w_1 = 0.0, mu_1 = 0.0; | |
| 7 | ||
| 8 | for (int i = 0; i < thresh; ++i) | |
| 9 | { | |
| 10 | w_0 += hist[i]; | |
| 11 | mu_0 += i * hist[i]; | |
| 12 | } | |
| 13 | for (int i = thresh; i < 256; ++i) | |
| 14 | { | |
| 15 | w_1 += hist[i]; | |
| 16 | mu_1 += i * hist[i]; | |
| 17 | } | |
| 18 | ||
| 19 | float mu_diff = (mu_0/w_0 - mu_1/w_1); | |
| 20 | return w_0*w_1*mu_diff*mu_diff; | |
| 21 | } | |
| 22 | ||
| 23 | #ifdef __SSE2__ | |
| 24 | static float _sse1_hadd_ps(__m128 v) | |
| 25 | { | |
| 26 | // Based on a StackOverflow answer. | |
| 27 | __m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1)); | |
| 28 | __m128 sums = _mm_add_ps(v, shuf); | |
| 29 | shuf = _mm_movehl_ps(shuf, sums); | |
| 30 | sums = _mm_add_ss(sums, shuf); | |
| 31 | return _mm_cvtss_f32(sums); | |
| 32 | } | |
| 33 | ||
| 34 | static float find_variance_sse2(float *hist, int thresh) | |
| 35 | { | |
| 36 | __m128 vw_0 = _mm_setzero_ps(), vmu_0 = _mm_setzero_ps(), | |
| 37 | vw_1 = _mm_setzero_ps(), vmu_1 = _mm_setzero_ps(); | |
| 38 | __m128 vcnt = _mm_set_ps(0, 1, 2, 3); | |
| 39 | ||
| 40 | int thresh_low = (thresh / 4 + 0) * 4, | |
| 41 | thresh_up = (thresh / 4 + 1) * 4; | |
| 42 | ||
| 43 | for (int i = 0; i < thresh_low; i+=4) | |
| 44 | { | |
| 45 | __m128 vhist = _mm_castsi128_ps(_mm_loadu_si128((void*)&hist[i])); | |
| 46 | ||
| 47 | vw_0 = _mm_add_ps(vhist, vw_0); | |
| 48 | vmu_0 = _mm_add_ps(_mm_mul_ps(vcnt, vhist), vw_0); | |
| 49 | vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); | |
| 50 | } | |
| 51 | ||
| 52 | // This is skipped, and handled as an edge case by non-SSE code. | |
| 53 | vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); | |
| 54 | ||
| 55 | for (int i = thresh_up; i < 256; i+=4) | |
| 56 | { | |
| 57 | __m128 vhist = _mm_castsi128_ps(_mm_loadu_si128((void*)&hist[i])); | |
| 58 | ||
| 59 | vw_1 = _mm_add_ps(vhist, vw_1); | |
| 60 | vmu_1 = _mm_add_ps(_mm_mul_ps(vcnt, vhist), vw_1); | |
| 61 | vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); | |
| 62 | } | |
| 63 | ||
| 64 | float w_0 = _sse1_hadd_ps(vw_0), mu_0 = _sse1_hadd_ps(vmu_0), | |
| 65 | w_1 = _sse1_hadd_ps(vw_1), mu_1 = _sse1_hadd_ps(vmu_1); | |
| 66 | ||
| 67 | // This edge case is here, because thresh may not be a multiple of 4. | |
| 68 | for (int i = thresh_low; i < thresh; ++i) | |
| 69 | { | |
| 70 | w_0 += hist[i]; | |
| 71 | mu_0 += i * hist[i]; | |
| 72 | } | |
| 73 | ||
| 74 | for (int i = thresh; i < thresh_up; ++i) | |
| 75 | { | |
| 76 | w_1 += hist[i]; | |
| 77 | mu_1 += i * hist[i]; | |
| 78 | } | |
| 79 | ||
| 80 | float mu_diff = (mu_0/w_0 - mu_1/w_1); | |
| 81 | return w_0*w_1*mu_diff*mu_diff; | |
| 82 | } | |
| 83 | #endif |
| 82 | 82 | *outpixel= (*pixel & 0x00FFFFFF); // copy all except alpha |
| 83 | 83 | |
| 84 | 84 | uint32_t d = distance(*pixel); // get distance |
| 85 | unsigned char a = 255; // default alpha | |
| 85 | unsigned char a = (*pixel >> 24); // default alpha | |
| 86 | 86 | if (d < distInt) { |
| 87 | 87 | a = 0; |
| 88 | 88 | if (d > distInt2) { |
| 99 | 99 | }; |
| 100 | 100 | |
| 101 | 101 | |
| 102 | frei0r::construct<bluescreen0r> plugin("bluescreen0r", "Color to alpha (blit SRCALPHA)", "Hedde Bosman",0,4,F0R_COLOR_MODEL_RGBA8888); | |
| 103 | ||
| 102 | frei0r::construct<bluescreen0r> plugin("bluescreen0r", | |
| 103 | "Color to alpha (blit SRCALPHA)", | |
| 104 | "Hedde Bosman", | |
| 105 | 0, 5, | |
| 106 | F0R_COLOR_MODEL_RGBA8888); | |
| 79 | 79 | { |
| 80 | 80 | float p[10]; |
| 81 | 81 | int i,j,m; |
| 82 | float zero = 0.0f; // MSVC doesn't allow division through a zero literal, but allows it through non-const variable set to zero | |
| 83 | 82 | |
| 84 | 83 | if ((x<xt[0])||(x>xt[t-1])) |
| 85 | 84 | { |
| 86 | 85 | // printf("\n\n x=%f je izven mej tabele!",x); |
| 87 | return 1.0/zero; | |
| 86 | // Return a reasonable value instead of dividing by zero | |
| 87 | if (x<xt[0]) return yt[0]; | |
| 88 | else return yt[t-1]; | |
| 88 | 89 | } |
| 89 | 90 | |
| 90 | 91 | //poisce, katere tocke bo uporabil |
| 97 | 98 | for (j=1;j<4;j++) |
| 98 | 99 | for (i=(4-1);i>=j;i--) |
| 99 | 100 | { |
| 100 | p[i]=p[i]+(x-xt[i+m])/(xt[i+m]-xt[i-j+m])*(p[i]-p[i-1]); | |
| 101 | // Check for division by zero | |
| 102 | float denominator = xt[i+m]-xt[i-j+m]; | |
| 103 | if (denominator == 0.0f) { | |
| 104 | // If denominator is zero, skip this iteration to avoid undefined behavior | |
| 105 | continue; | |
| 106 | } | |
| 107 | p[i]=p[i]+(x-xt[i+m])/denominator*(p[i]-p[i-1]); | |
| 101 | 108 | } |
| 102 | 109 | return p[4-1]; |
| 103 | 110 | } |
| 45 | 45 | uint32_t size; |
| 46 | 46 | } ScreenGeometry; |
| 47 | 47 | |
| 48 | #define PIXELAT(x1,y1,s) ((s)+(x1)+ yprecal[y1])// (y1)*(geo->w))) | |
| 48 | #define PIXELAT(x1,y1,s,inst) ((s)+(x1)+ inst->yprecal[y1])// (y1)*(geo->w))) | |
| 49 | 49 | #define GMERROR(cc1,cc2) ((((RED(cc1)-RED(cc2))*(RED(cc1)-RED(cc2))) + \ |
| 50 | 50 | ((GREEN(cc1)-GREEN(cc2)) *(GREEN(cc1)-GREEN(cc2))) + \ |
| 51 | 51 | ((BLUE(cc1)-BLUE(cc2))*(BLUE(cc1)-BLUE(cc2))))) |
| 154 | 154 | long error,max=0; |
| 155 | 155 | |
| 156 | 156 | /* Assumes PrePixelModify has been run */ |
| 157 | c1 = *PIXELAT(x-m_diffspace,y,src); | |
| 158 | c2 = *PIXELAT(x+m_diffspace,y,src); | |
| 157 | c1 = *PIXELAT(x-m_diffspace,y,src,this); | |
| 158 | c2 = *PIXELAT(x+m_diffspace,y,src,this); | |
| 159 | 159 | error = GMERROR(c1,c2); |
| 160 | 160 | if (error>max) max = error; |
| 161 | ||
| 162 | c1 = *PIXELAT(x,y-m_diffspace,src); | |
| 163 | c2 = *PIXELAT(x,y+m_diffspace,src); | |
| 161 | ||
| 162 | c1 = *PIXELAT(x,y-m_diffspace,src,this); | |
| 163 | c2 = *PIXELAT(x,y+m_diffspace,src,this); | |
| 164 | 164 | error = GMERROR(c1,c2); |
| 165 | 165 | if (error>max) max = error; |
| 166 | ||
| 167 | c1 = *PIXELAT(x-m_diffspace,y-m_diffspace,src); | |
| 168 | c2 = *PIXELAT(x+m_diffspace,y+m_diffspace,src); | |
| 166 | ||
| 167 | c1 = *PIXELAT(x-m_diffspace,y-m_diffspace,src,this); | |
| 168 | c2 = *PIXELAT(x+m_diffspace,y+m_diffspace,src,this); | |
| 169 | 169 | error = GMERROR(c1,c2); |
| 170 | 170 | if (error>max) max = error; |
| 171 | ||
| 172 | c1 = *PIXELAT(x+m_diffspace,y-m_diffspace,src); | |
| 173 | c2 = *PIXELAT(x-m_diffspace,y+m_diffspace,src); | |
| 171 | ||
| 172 | c1 = *PIXELAT(x+m_diffspace,y-m_diffspace,src,this); | |
| 173 | c2 = *PIXELAT(x-m_diffspace,y+m_diffspace,src,this); | |
| 174 | 174 | error = GMERROR(c1,c2); |
| 175 | 175 | if (error>max) max = error; |
| 176 | ||
| 176 | ||
| 177 | 177 | return(max); |
| 178 | 178 | } |
| 179 | 179 | |
| 124 | 124 | float b1 = inst->color.b * 255.0; |
| 125 | 125 | float r2, g2, b2; |
| 126 | 126 | int l; |
| 127 | /* Scale factor to normalize distance to 0-255 range: | |
| 128 | 0.705724361914764 ≈ 255.0 / sqrt(3 * 255^2) = 255.0 / (255.0 * sqrt(3)) */ | |
| 129 | const float SCALE_FACTOR = 0.705724361914764; | |
| 127 | 130 | while (len--) { |
| 128 | 131 | r2 = *src++; |
| 129 | 132 | g2 = *src++; |
| 130 | 133 | b2 = *src++; |
| 131 | l = (int)rint( sqrtf( powf( r1 - r2, 2 ) + powf( g1 - g2, 2 ) + powf( b1 - b2, 2 ) ) * 0.705724361914764 ); | |
| 132 | /* Hint 0.35320727852735 == 255.0 / sqrt( (255)**2 + (255)**2 + (255)*2 )*/ | |
| 133 | if ( r1 < 0 || r1 > 255 || g1 < 0 || g1 > 255 || b1 < 0 || b1 > 255 || r2 < 0 || r2 > 255 || g2 < 0 || g2 > 255 || b2 < 0 || b2 > 255 ) { | |
| 134 | printf ("%f %f %f\n", r2, g2, b2 ); | |
| 135 | } | |
| 134 | l = (int)rint( sqrtf( powf( r1 - r2, 2 ) + powf( g1 - g2, 2 ) + powf( b1 - b2, 2 ) ) * SCALE_FACTOR ); | |
| 136 | 135 | |
| 136 | // Clamp result to valid range | |
| 137 | l = CLAMP(l, 0, 255); | |
| 137 | 138 | |
| 138 | 139 | *dst++ = (unsigned char) (l); |
| 139 | 140 | *dst++ = (unsigned char) (l); |
| 253 | 253 | step_line_u = (int32_t) ((u_right-u_left) >> GRID_SIZE_LOG); |
| 254 | 254 | step_line_v = (int32_t) ((v_right-v_left) >> GRID_SIZE_LOG); |
| 255 | 255 | |
| 256 | for(block_x=0; block_x < GRID_SIZE; ++block_x) | |
| 257 | { | |
| 256 | for(block_x=0; block_x < GRID_SIZE; ++block_x) | |
| 257 | { | |
| 258 | 258 | int uu = u_line_index >> 16; |
| 259 | 259 | int vv = v_line_index >> 16; |
| 260 | ||
| 261 | u_line_index += step_line_u; | |
| 262 | v_line_index += step_line_v; | |
| 263 | ||
| 264 | *pos++ = src[uu + vv * w]; | |
| 265 | } | |
| 260 | ||
| 261 | u_line_index += step_line_u; | |
| 262 | v_line_index += step_line_v; | |
| 263 | ||
| 264 | // Bounds checking to prevent buffer overrun | |
| 265 | if (uu >= 0 && uu < (int)w && vv >= 0 && vv < (int)h) { | |
| 266 | *pos++ = src[uu + vv * w]; | |
| 267 | } else { | |
| 268 | *pos++ = 0; // Black pixel for out-of-bounds access | |
| 269 | } | |
| 270 | } | |
| 266 | 271 | |
| 267 | 272 | start_col_uu += step_start_col_u; |
| 268 | 273 | end_col_uu += step_end_col_u; |
| 0 | set (SOURCES facebl0r.cpp) | |
| 1 | set (TARGET facebl0r) | |
| 0 | # facebl0r filter - requires OpenCV (and protobuf) | |
| 1 | # This filter can cause conflicts with applications that also use protobuf | |
| 2 | # such as MLT, so it can be disabled with -DWITHOUT_FACERECOGNITION=ON | |
| 2 | 3 | |
| 3 | if (MSVC) | |
| 4 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 5 | endif (MSVC) | |
| 4 | if (OpenCV_FOUND AND NOT WITHOUT_FACERECOGNITION) | |
| 5 | set (SOURCES facebl0r.cpp) | |
| 6 | set (TARGET facebl0r) | |
| 6 | 7 | |
| 7 | include_directories(${OpenCV_INCLUDE_DIRS}) | |
| 8 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 9 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 10 | target_link_libraries(${TARGET} ${OpenCV_LIBS}) | |
| 8 | if (MSVC) | |
| 9 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 10 | endif (MSVC) | |
| 11 | 11 | |
| 12 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) | |
| 12 | include_directories(${OpenCV_INCLUDE_DIRS}) | |
| 13 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 14 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 15 | target_link_libraries(${TARGET} ${OpenCV_LIBS}) | |
| 16 | ||
| 17 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) | |
| 18 | endif() |
| 0 | set (SOURCES facedetect.cpp) | |
| 1 | set (TARGET facedetect) | |
| 0 | # facedetect filter - requires OpenCV (and protobuf) | |
| 1 | # This filter can cause conflicts with applications that also use protobuf | |
| 2 | # such as MLT, so it can be disabled with -DWITHOUT_FACERECOGNITION=ON | |
| 2 | 3 | |
| 3 | if (MSVC) | |
| 4 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 5 | endif (MSVC) | |
| 4 | if (OpenCV_FOUND AND NOT WITHOUT_FACERECOGNITION) | |
| 5 | set (SOURCES facedetect.cpp) | |
| 6 | set (TARGET facedetect) | |
| 6 | 7 | |
| 7 | include_directories(${OpenCV_INCLUDE_DIRS}) | |
| 8 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 9 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 10 | target_link_libraries(${TARGET} ${OpenCV_LIBS}) | |
| 8 | if (MSVC) | |
| 9 | set (SOURCES ${SOURCES} ${FREI0R_DEF}) | |
| 10 | endif (MSVC) | |
| 11 | 11 | |
| 12 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) | |
| 12 | include_directories(${OpenCV_INCLUDE_DIRS}) | |
| 13 | add_library (${TARGET} MODULE ${SOURCES}) | |
| 14 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 15 | target_link_libraries(${TARGET} ${OpenCV_LIBS}) | |
| 16 | ||
| 17 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) | |
| 18 | endif() |
| 44 | 44 | short int howToDistort1; |
| 45 | 45 | short int howToDistort2; |
| 46 | 46 | short int passThisLine; |
| 47 | } g0r_state; | |
| 47 | }; | |
| 48 | 48 | |
| 49 | 49 | typedef struct glitch0r_instance |
| 50 | 50 | { |
| 57 | 57 | short int colorGlitchIntensity; |
| 58 | 58 | short int doColorDistortion; |
| 59 | 59 | short int glitchChance; |
| 60 | ||
| 61 | struct glitch0r_state state; // Instance-specific state | |
| 60 | 62 | } glitch0r_instance_t; |
| 61 | 63 | |
| 62 | 64 | |
| 67 | 69 | |
| 68 | 70 | inline static void glitch0r_state_reset(glitch0r_instance_t *inst) |
| 69 | 71 | { |
| 70 | g0r_state.currentPos = 0; | |
| 71 | g0r_state.currentBlock = rnd(1, inst->maxBlockSize); | |
| 72 | g0r_state.blkShift = rnd(1, inst->maxBlockShift); | |
| 73 | g0r_state.passThisLine = (inst->glitchChance < rnd(1, 101)) ? 1 : 0; | |
| 72 | inst->state.currentPos = 0; | |
| 73 | inst->state.currentBlock = rnd(1, inst->maxBlockSize); | |
| 74 | inst->state.blkShift = rnd(1, inst->maxBlockShift); | |
| 75 | inst->state.passThisLine = (inst->glitchChance < rnd(1, 101)) ? 1 : 0; | |
| 74 | 76 | |
| 75 | 77 | if (inst->doColorDistortion) |
| 76 | 78 | { |
| 77 | g0r_state.distortionSeed1 = rnd(0x00000000, 0xfffffffe); | |
| 78 | g0r_state.distortionSeed2 = rnd(0x00000000, 0xfffffffe); | |
| 79 | g0r_state.howToDistort1 = rnd (0, inst->colorGlitchIntensity); | |
| 80 | g0r_state.howToDistort2 = rnd (0, inst->colorGlitchIntensity); | |
| 79 | inst->state.distortionSeed1 = rnd(0x00000000, 0xfffffffe); | |
| 80 | inst->state.distortionSeed2 = rnd(0x00000000, 0xfffffffe); | |
| 81 | inst->state.howToDistort1 = rnd (0, inst->colorGlitchIntensity); | |
| 82 | inst->state.howToDistort2 = rnd (0, inst->colorGlitchIntensity); | |
| 81 | 83 | } |
| 82 | 84 | } |
| 83 | 85 | |
| 303 | 305 | |
| 304 | 306 | uint32_t* dst = outframe; |
| 305 | 307 | const uint32_t* src = inframe; |
| 306 | uint32_t *pixel; | |
| 307 | ||
| 308 | g0r_state.currentBlock = rnd(1, inst->maxBlockSize); | |
| 308 | uint32_t *pixel; | |
| 309 | ||
| 310 | inst->state.currentBlock = rnd(1, inst->maxBlockSize); | |
| 309 | 311 | |
| 310 | 312 | for (y = 0; y < inst->height; y++) |
| 311 | 313 | { |
| 312 | 314 | |
| 313 | if (g0r_state.currentPos > g0r_state.currentBlock) | |
| 315 | if (inst->state.currentPos > inst->state.currentBlock) | |
| 314 | 316 | { |
| 315 | 317 | glitch0r_state_reset(inst); |
| 316 | 318 | } |
| 317 | 319 | else |
| 318 | g0r_state.currentPos++; | |
| 319 | ||
| 320 | g0r_state.currentY = y*inst->width; | |
| 321 | pixel = dst + g0r_state.currentY; | |
| 322 | ||
| 323 | if (g0r_state.passThisLine) | |
| 324 | { | |
| 325 | memcpy((uint32_t *)(dst + g0r_state.currentY), | |
| 326 | (uint32_t *)(src + g0r_state.currentY), | |
| 320 | inst->state.currentPos++; | |
| 321 | ||
| 322 | inst->state.currentY = y*inst->width; | |
| 323 | pixel = dst + inst->state.currentY; | |
| 324 | ||
| 325 | if (inst->state.passThisLine) | |
| 326 | { | |
| 327 | memcpy((uint32_t *)(dst + inst->state.currentY), | |
| 328 | (uint32_t *)(src + inst->state.currentY), | |
| 327 | 329 | (inst->width) * sizeof(uint32_t)); |
| 328 | 330 | continue; |
| 329 | 331 | } |
| 330 | 332 | |
| 331 | for (x = g0r_state.blkShift; x < (inst->width); x++) | |
| 332 | { | |
| 333 | *(pixel) = *(src + g0r_state.currentY + x); | |
| 333 | for (x = inst->state.blkShift; x < (inst->width); x++) | |
| 334 | { | |
| 335 | *(pixel) = *(src + inst->state.currentY + x); | |
| 334 | 336 | |
| 335 | 337 | if (inst->doColorDistortion) |
| 336 | 338 | glitch0r_pixel_dist0rt(pixel, |
| 337 | g0r_state.distortionSeed1, g0r_state.howToDistort1); | |
| 339 | inst->state.distortionSeed1, inst->state.howToDistort1); | |
| 338 | 340 | |
| 339 | 341 | pixel++; |
| 340 | 342 | } |
| 341 | 343 | |
| 342 | for (x = 0; x < g0r_state.blkShift; x++) | |
| 343 | { | |
| 344 | *(pixel) = *(src + g0r_state.currentY + x); | |
| 344 | for (x = 0; x < inst->state.blkShift; x++) | |
| 345 | { | |
| 346 | *(pixel) = *(src + inst->state.currentY + x); | |
| 345 | 347 | |
| 346 | 348 | if (inst->doColorDistortion) |
| 347 | 349 | glitch0r_pixel_dist0rt(pixel, |
| 348 | g0r_state.distortionSeed2, g0r_state.howToDistort2); | |
| 350 | inst->state.distortionSeed2, inst->state.howToDistort2); | |
| 349 | 351 | |
| 350 | 352 | pixel++; |
| 351 | 353 | } |
| 5 | 5 | endif (MSVC) |
| 6 | 6 | |
| 7 | 7 | add_library (${TARGET} MODULE ${SOURCES}) |
| 8 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 8 | 9 | |
| 9 | # No «lib» prefix (name.so instead of libname.so) | |
| 10 | set_target_properties (${TARGET} PROPERTIES PREFIX "") | |
| 10 | install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) |
| 95 | 95 | info->major_version = 0; |
| 96 | 96 | info->minor_version = 1; |
| 97 | 97 | info->num_params = 4; |
| 98 | info->explanation = "Performs a continious trichromatic tinting"; | |
| 98 | info->explanation = "Performs a continuous trichromatic tinting"; | |
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 101 | void f0r_get_param_info(f0r_param_info_t* info, int param_index) |
| 121 | 121 | levelsInput = CLAMP(levelsInput, 0.0, 48.0) + 2.0; |
| 122 | 122 | int numLevels = (int)levelsInput; |
| 123 | 123 | |
| 124 | // Prevent division by zero | |
| 125 | if (numLevels < 2) numLevels = 2; | |
| 126 | ||
| 124 | 127 | // create levels table |
| 125 | 128 | unsigned char levels[256]; |
| 126 | 129 | int i; |
| 2 | 2 | * It contains code from plug-ins/common/noise-rgb.c, see that for copyrights. |
| 3 | 3 | * |
| 4 | 4 | * rgbnoise.c |
| 5 | * Copyright 2012 Janne Liljeblad | |
| 5 | * Copyright 2012 Janne Liljeblad | |
| 6 | 6 | * |
| 7 | 7 | * This file is a Frei0r plugin. |
| 8 | 8 | * |
| 28 | 28 | #include "frei0r.h" |
| 29 | 29 | #include "frei0r/math.h" |
| 30 | 30 | |
| 31 | static int MY_MAX_RAND = 32767;// I assume RAND_MAX to be at least this big. | |
| 32 | static double gaussian_lookup[32767]; | |
| 33 | static int TABLE_INITED = 0; | |
| 34 | static int next_gaussian_index = 0; | |
| 35 | static int last_in_range = 32766; | |
| 31 | #define MY_MAX_RAND 32767 // assume RAND_MAX to be at least this big. | |
| 36 | 32 | |
| 37 | 33 | typedef struct rgbnoise_instance |
| 38 | 34 | { |
| 39 | 35 | unsigned int width; |
| 40 | 36 | unsigned int height; |
| 41 | 37 | double noise; |
| 38 | double gaussian_lookup[MY_MAX_RAND]; | |
| 39 | int table_inited; | |
| 40 | int next_gaussian_index; | |
| 41 | int last_in_range; | |
| 42 | 42 | } rgbnoise_instance_t; |
| 43 | 43 | |
| 44 | 44 | |
| 53 | 53 | rgbnoiseInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; |
| 54 | 54 | rgbnoiseInfo->color_model = F0R_COLOR_MODEL_RGBA8888; |
| 55 | 55 | rgbnoiseInfo->frei0r_version = FREI0R_MAJOR_VERSION; |
| 56 | rgbnoiseInfo->major_version = 0; | |
| 57 | rgbnoiseInfo->minor_version = 9; | |
| 58 | rgbnoiseInfo->num_params = 1; | |
| 56 | rgbnoiseInfo->major_version = 0; | |
| 57 | rgbnoiseInfo->minor_version = 9; | |
| 58 | rgbnoiseInfo->num_params = 1; | |
| 59 | 59 | rgbnoiseInfo->explanation = "Adds RGB noise to image."; |
| 60 | 60 | } |
| 61 | 61 | |
| 73 | 73 | f0r_instance_t f0r_construct(unsigned int width, unsigned int height) |
| 74 | 74 | { |
| 75 | 75 | rgbnoise_instance_t* inst = (rgbnoise_instance_t*)calloc(1, sizeof(*inst)); |
| 76 | inst->width = width; | |
| 76 | inst->width = width; | |
| 77 | 77 | inst->height = height; |
| 78 | 78 | inst->noise = 0.2; |
| 79 | inst->table_inited = 0; | |
| 80 | inst->next_gaussian_index = 0; | |
| 81 | inst->last_in_range = MY_MAX_RAND; | |
| 79 | 82 | return (f0r_instance_t)inst; |
| 80 | 83 | } |
| 81 | 84 | |
| 84 | 87 | free(instance); |
| 85 | 88 | } |
| 86 | 89 | |
| 87 | void f0r_set_param_value(f0r_instance_t instance, | |
| 90 | void f0r_set_param_value(f0r_instance_t instance, | |
| 88 | 91 | f0r_param_t param, int param_index) |
| 89 | 92 | { |
| 90 | 93 | rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; |
| 98 | 101 | |
| 99 | 102 | void f0r_get_param_value(f0r_instance_t instance, |
| 100 | 103 | f0r_param_t param, int param_index) |
| 101 | { | |
| 104 | { | |
| 102 | 105 | rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; |
| 103 | switch (param_index) | |
| 106 | switch (param_index) | |
| 104 | 107 | { |
| 105 | 108 | case 0: |
| 106 | 109 | *((double*)param) = inst->noise; |
| 132 | 135 | return x; |
| 133 | 136 | } |
| 134 | 137 | |
| 135 | static void create_new_lookup_range() | |
| 138 | static void create_new_lookup_range(rgbnoise_instance_t* inst) | |
| 136 | 139 | { |
| 137 | 140 | int first, last, tmp; |
| 138 | 141 | first = rand() % (MY_MAX_RAND - 1); |
| 143 | 146 | last = first; |
| 144 | 147 | first = tmp; |
| 145 | 148 | } |
| 146 | next_gaussian_index = first; | |
| 147 | last_in_range = last; | |
| 148 | } | |
| 149 | ||
| 150 | static inline double next_gauss() | |
| 151 | { | |
| 152 | next_gaussian_index++; | |
| 153 | if (next_gaussian_index >= last_in_range) | |
| 154 | { | |
| 155 | create_new_lookup_range(); | |
| 156 | } | |
| 157 | return gaussian_lookup[next_gaussian_index]; | |
| 158 | } | |
| 159 | ||
| 160 | static inline int addNoise(int sample, double noise) | |
| 149 | inst->next_gaussian_index = first; | |
| 150 | inst->last_in_range = last; | |
| 151 | } | |
| 152 | ||
| 153 | static inline double next_gauss(rgbnoise_instance_t* inst) | |
| 154 | { | |
| 155 | inst->next_gaussian_index++; | |
| 156 | if (inst->next_gaussian_index >= inst->last_in_range) | |
| 157 | { | |
| 158 | create_new_lookup_range(inst); | |
| 159 | } | |
| 160 | return inst->gaussian_lookup[inst->next_gaussian_index]; | |
| 161 | } | |
| 162 | ||
| 163 | static inline int addNoise(rgbnoise_instance_t* inst, int sample, double noise) | |
| 161 | 164 | { |
| 162 | 165 | int byteNoise = 0; |
| 163 | 166 | int noiseSample = 0; |
| 164 | 167 | |
| 165 | byteNoise = (int) (noise * next_gauss()); | |
| 168 | byteNoise = (int) (noise * next_gauss(inst)); | |
| 166 | 169 | noiseSample = sample + byteNoise; |
| 167 | 170 | noiseSample = CLAMP(noiseSample, 0, 255); |
| 168 | 171 | return noiseSample; |
| 169 | } | |
| 172 | } | |
| 170 | 173 | |
| 171 | 174 | int f0r_init() |
| 172 | 175 | { |
| 173 | if (TABLE_INITED == 0) | |
| 176 | return 1; | |
| 177 | } | |
| 178 | ||
| 179 | void rgb_noise(f0r_instance_t instance, double time, | |
| 180 | const uint32_t* inframe, uint32_t* outframe) | |
| 181 | { | |
| 182 | rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; | |
| 183 | unsigned int len = inst->width * inst->height; | |
| 184 | ||
| 185 | // Initialize the gaussian lookup table if not already done | |
| 186 | if (inst->table_inited == 0) | |
| 174 | 187 | { |
| 175 | 188 | int i; |
| 176 | 189 | for( i = 0; i < MY_MAX_RAND; i++) |
| 177 | 190 | { |
| 178 | gaussian_lookup[i] = gauss() * 127.0; | |
| 191 | inst->gaussian_lookup[i] = gauss() * 127.0; | |
| 179 | 192 | } |
| 180 | TABLE_INITED = 1; | |
| 181 | } | |
| 182 | return 1; | |
| 183 | } | |
| 184 | ||
| 185 | void rgb_noise(f0r_instance_t instance, double time, | |
| 186 | const uint32_t* inframe, uint32_t* outframe) | |
| 187 | { | |
| 188 | rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; | |
| 189 | unsigned int len = inst->width * inst->height; | |
| 193 | inst->table_inited = 1; | |
| 194 | inst->next_gaussian_index = 0; | |
| 195 | inst->last_in_range = MY_MAX_RAND; | |
| 196 | } | |
| 190 | 197 | |
| 191 | 198 | unsigned char* dst = (unsigned char*)outframe; |
| 192 | 199 | const unsigned char* src = (unsigned char*)inframe; |
| 196 | 203 | while (len--) |
| 197 | 204 | { |
| 198 | 205 | sample = *src++; |
| 199 | *dst++ = addNoise(sample, noise); | |
| 206 | *dst++ = addNoise(inst, sample, noise); | |
| 200 | 207 | sample = *src++; |
| 201 | *dst++ = addNoise(sample, noise); | |
| 208 | *dst++ = addNoise(inst, sample, noise); | |
| 202 | 209 | sample = *src++; |
| 203 | *dst++ = addNoise(sample, noise); | |
| 210 | *dst++ = addNoise(inst, sample, noise); | |
| 204 | 211 | *dst++ = *src++; |
| 205 | 212 | } |
| 206 | 213 | } |
| 212 | 219 | assert(instance); |
| 213 | 220 | rgb_noise(instance, time, inframe, outframe); |
| 214 | 221 | } |
| 215 | ||
| 0 | 0 | /* rgbsplit0r.c |
| 1 | 1 | * Copyright (C) 2016 IDENT Software ~ http://identsoft.org |
| 2 | 2 | * Inspired by the witch house and web culture |
| 3 | * | |
| 3 | * | |
| 4 | 4 | * This file is a Frei0r plugin. |
| 5 | 5 | * |
| 6 | 6 | * This program is free software; you can redistribute it and/or modify |
| 110 | 110 | inst->width = width; inst->height = height; |
| 111 | 111 | inst->shiftY = 0; |
| 112 | 112 | inst->shiftX = 0; |
| 113 | ||
| 114 | 113 | return (f0r_instance_t)inst; |
| 115 | 114 | } |
| 116 | 115 | |
| 119 | 118 | free(instance); |
| 120 | 119 | } |
| 121 | 120 | |
| 122 | void f0r_set_param_value(f0r_instance_t instance, | |
| 121 | void f0r_set_param_value(f0r_instance_t instance, | |
| 123 | 122 | f0r_param_t param, int param_index) |
| 124 | 123 | { |
| 125 | 124 | assert(instance); |
| 134 | 133 | double shiftY = *((double*)param) - 0.5; |
| 135 | 134 | |
| 136 | 135 | // Convert to range from 0 to one eighth of height |
| 137 | shiftY = ((inst->height / 8) * shiftY); | |
| 136 | if (inst->height > 0) | |
| 137 | shiftY = ((inst->height / 8) * shiftY); | |
| 138 | else | |
| 139 | shiftY = 0; | |
| 138 | 140 | |
| 139 | 141 | inst->shiftY = (unsigned int)shiftY; |
| 140 | 142 | break; |
| 144 | 146 | { |
| 145 | 147 | // scale to [-1/16..1/16] |
| 146 | 148 | double shiftX = *((double*)param) - 0.5; |
| 147 | ||
| 149 | ||
| 148 | 150 | // Convert to range from 0 to one eighth of width |
| 149 | shiftX = ((inst->width / 8) * shiftX); | |
| 151 | if (inst->width > 0) | |
| 152 | shiftX = ((inst->width / 8) * shiftX); | |
| 153 | else | |
| 154 | shiftX = 0; | |
| 150 | 155 | |
| 151 | 156 | inst->shiftX = (unsigned int)shiftX; |
| 152 | 157 | break; |
| 166 | 171 | case 0 : // vertical shift |
| 167 | 172 | { |
| 168 | 173 | // convert plugin's param to frei0r range |
| 169 | *((double*)param) = (inst->shiftY) / (inst->height / 8) + 0.5; | |
| 174 | if (inst->height > 0) | |
| 175 | *((double*)param) = (inst->shiftY) / (inst->height / 8) + 0.5; | |
| 176 | else | |
| 177 | *((double*)param) = 0.5; | |
| 170 | 178 | break; |
| 171 | 179 | } |
| 172 | 180 | |
| 173 | 181 | case 1 : // horizontal shift |
| 174 | 182 | { |
| 175 | 183 | // convert plugin's param to frei0r range |
| 176 | *((double*)param) = (inst->shiftX) / (inst->width / 8) + 0.5; | |
| 184 | if (inst->width > 0) | |
| 185 | *((double*)param) = (inst->shiftX) / (inst->width / 8) + 0.5; | |
| 186 | else | |
| 187 | *((double*)param) = 0.5; | |
| 177 | 188 | break; |
| 178 | 189 | } |
| 179 | 190 | } |
| 194 | 205 | uint32_t pxR = 0, pxG = 0, pxB = 0; |
| 195 | 206 | |
| 196 | 207 | // First make a blue layer shifted back |
| 197 | if (((x - inst->shiftX) < inst->width) && | |
| 198 | ((y - inst->shiftY) < inst->height)) | |
| 208 | if (((int)x >= (int)inst->shiftX) && | |
| 209 | ((int)y >= (int)inst->shiftY)) | |
| 199 | 210 | { |
| 200 | 211 | rgbsplit0r_extract_color((uint32_t *)(src + |
| 201 | 212 | (x - inst->shiftX) + |
| 220 | 231 | *(dst + x + (y*inst->width)) = (pxG | pxB | pxR); |
| 221 | 232 | } |
| 222 | 233 | } |
| 223 | ||
| 25 | 25 | public: |
| 26 | 26 | sobel(unsigned int width, unsigned int height) |
| 27 | 27 | { |
| 28 | this->width = width; | |
| 29 | this->height = height; | |
| 28 | 30 | } |
| 29 | 31 | |
| 30 | 32 | virtual void update(double time, |
| 31 | 33 | uint32_t* out, |
| 32 | 34 | const uint32_t* in) |
| 33 | 35 | { |
| 36 | if (width == 0 || height == 0) return; | |
| 37 | ||
| 34 | 38 | std::copy(in, in + width*height, out); |
| 35 | 39 | for (unsigned int y=1; y<height-1; ++y) |
| 36 | 40 | { |
| 45 | 49 | unsigned char *p7 = (unsigned char *)&in[(y+1)*width+(x-1)]; |
| 46 | 50 | unsigned char *p8 = (unsigned char *)&in[(y+1)*width+x]; |
| 47 | 51 | unsigned char *p9 = (unsigned char *)&in[(y+1)*width+(x+1)]; |
| 48 | ||
| 52 | ||
| 49 | 53 | unsigned char *g = (unsigned char *)&out[y*width+x]; |
| 50 | ||
| 54 | ||
| 51 | 55 | for (int i=0; i<3; ++i) |
| 52 | 56 | g[i] = CLAMP0255( |
| 53 | 57 | abs(p1[i] + p2[i]*2 + p3[i] - p7[i] - p8[i]*2 - p9[i]) + |
| 21 | 21 | #include <stdlib.h> |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | |
| 24 | #ifdef __SSE4_1__ | |
| 24 | /* Check for SSE4.1 support */ | |
| 25 | #if defined(__SSE4_1__) | |
| 25 | 26 | #include <smmintrin.h> |
| 27 | #define USE_SSE4_1 1 | |
| 28 | #else | |
| 29 | #define USE_SSE4_1 0 | |
| 30 | #endif | |
| 31 | ||
| 32 | /* Check for other SIMD instruction sets */ | |
| 33 | #if defined(__AVX__) && defined(__AVX2__) | |
| 34 | #include <immintrin.h> | |
| 35 | #define USE_AVX2 1 | |
| 36 | #else | |
| 37 | #define USE_AVX2 0 | |
| 38 | #endif | |
| 39 | ||
| 40 | #if defined(__ARM_NEON__) || defined(__ARM_NEON) | |
| 41 | #include <arm_neon.h> | |
| 42 | #define USE_NEON 1 | |
| 43 | #else | |
| 44 | #define USE_NEON 0 | |
| 26 | 45 | #endif |
| 27 | 46 | |
| 28 | 47 | #include <frei0r.h> |
| 45 | 64 | void f0r_deinit() |
| 46 | 65 | { /* no initialization required */ } |
| 47 | 66 | |
| 48 | void f0r_get_plugin_info(f0r_plugin_info_t* tint0r_instance_t) | |
| 49 | { | |
| 50 | tint0r_instance_t->name = "Tint0r"; | |
| 51 | tint0r_instance_t->author = "Maksim Golovkin & Cynthia"; | |
| 52 | tint0r_instance_t->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 53 | tint0r_instance_t->color_model = F0R_COLOR_MODEL_BGRA8888; | |
| 54 | tint0r_instance_t->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 55 | tint0r_instance_t->major_version = 0; | |
| 56 | tint0r_instance_t->minor_version = 1; | |
| 57 | tint0r_instance_t->num_params = 3; | |
| 58 | tint0r_instance_t->explanation = "Tint a source image with specified colors"; | |
| 67 | void f0r_get_plugin_info(f0r_plugin_info_t* info) | |
| 68 | { | |
| 69 | info->name = "Tint0r"; | |
| 70 | info->author = "Maksim Golovkin & Cynthia"; | |
| 71 | info->plugin_type = F0R_PLUGIN_TYPE_FILTER; | |
| 72 | info->color_model = F0R_COLOR_MODEL_BGRA8888; | |
| 73 | info->frei0r_version = FREI0R_MAJOR_VERSION; | |
| 74 | info->major_version = 0; | |
| 75 | info->minor_version = 1; | |
| 76 | info->num_params = 3; | |
| 77 | info->explanation = "Tint a source image with specified colors"; | |
| 59 | 78 | } |
| 60 | 79 | |
| 61 | 80 | void f0r_get_param_info(f0r_param_info_t* info, int param_index) |
| 142 | 161 | } |
| 143 | 162 | } |
| 144 | 163 | |
| 145 | #ifndef __SSE4_1__ | |
| 146 | 164 | static inline unsigned char map_color(double amount, double comp_amount, float color, float luma, float minColor, float maxColor) |
| 147 | 165 | { |
| 148 | 166 | double val = (comp_amount * color) + amount * (luma * (maxColor - minColor) + minColor); |
| 149 | 167 | return (unsigned char)(255*CLAMP(val, 0, 1)); |
| 150 | 168 | } |
| 151 | #endif | |
| 152 | ||
| 153 | void f0r_update(f0r_instance_t instance, double time, | |
| 154 | const uint32_t* inframe, uint32_t* outframe) | |
| 155 | { | |
| 156 | assert(instance); | |
| 157 | tint0r_instance_t* inst = (tint0r_instance_t*)instance; | |
| 158 | ||
| 159 | #ifdef __SSE4_1__ | |
| 160 | size_t len = (inst->width * inst->height) / 4; | |
| 161 | ||
| 162 | const __m128 weights = _mm_set_ps(0.0, 0.299, 0.587, 0.114), | |
| 163 | amount = _mm_set1_ps(inst->amount), | |
| 169 | ||
| 170 | #if USE_SSE4_1 | |
| 171 | static void tint_sse41(const uint32_t* inframe, uint32_t* outframe, size_t len, | |
| 172 | double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) | |
| 173 | { | |
| 174 | const __m128 weights = _mm_set_ps(0.0, 0.114, 0.587, 0.299), | |
| 175 | sse_amount = _mm_set1_ps(amount), | |
| 164 | 176 | /* Pass the alpha channel */ |
| 165 | 177 | comp_amount = _mm_set_ps(1.0, |
| 166 | 1.0 - inst->amount, | |
| 167 | 1.0 - inst->amount, | |
| 168 | 1.0 - inst->amount); | |
| 169 | ||
| 170 | f0r_param_color_t black = inst->blackColor, | |
| 171 | white = inst->whiteColor; | |
| 178 | 1.0 - amount, | |
| 179 | 1.0 - amount, | |
| 180 | 1.0 - amount); | |
| 172 | 181 | |
| 173 | 182 | /* Zero the alpha component to exclude it from calculations. */ |
| 174 | const __m128 cmin = _mm_set_ps(0.0, black.r, black.g, black.b), | |
| 175 | cdelta = _mm_sub_ps(_mm_set_ps(0.0, white.r, white.g, white.b), cmin), | |
| 176 | tmp0 = _mm_mul_ps(cdelta, amount), | |
| 177 | tmp1 = _mm_mul_ps(_mm_mul_ps(amount, _mm_set1_ps(255.0)), cmin); | |
| 183 | const __m128 cmin = _mm_set_ps(0.0, blackColor.b, blackColor.g, blackColor.r), | |
| 184 | cdelta = _mm_sub_ps(_mm_set_ps(0.0, whiteColor.b, whiteColor.g, whiteColor.r), cmin), | |
| 185 | tmp0 = _mm_mul_ps(cdelta, sse_amount), | |
| 186 | tmp1 = _mm_mul_ps(_mm_mul_ps(sse_amount, _mm_set1_ps(255.0)), cmin); | |
| 178 | 187 | |
| 179 | 188 | __m128 p, p0, p1, p2, p3, luma; |
| 180 | #else | |
| 181 | unsigned int len = inst->width * inst->height; | |
| 182 | double amount = inst->amount; | |
| 183 | double comp_amount = 1.0 - inst->amount; | |
| 184 | ||
| 185 | unsigned char* dst = (unsigned char*)outframe; | |
| 186 | const unsigned char* src = (unsigned char*)inframe; | |
| 187 | float b, g, r; | |
| 188 | float luma; | |
| 189 | #endif | |
| 190 | ||
| 191 | while (len--) | |
| 192 | { | |
| 193 | #ifdef __SSE4_1__ | |
| 189 | ||
| 190 | // Process pixels in groups of 4 | |
| 191 | for (size_t i = 0; i < len; i++) | |
| 192 | { | |
| 194 | 193 | /* Load four pixels at once. */ |
| 195 | p = _mm_loadu_si128((__m128i*)inframe); | |
| 194 | p = _mm_loadu_si128((__m128i*)(inframe + i * 4)); | |
| 196 | 195 | |
| 197 | 196 | /* Extract four pixels into separate XMM registers and convert them to float. */ |
| 198 | 197 | p0 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(p)); |
| 199 | 198 | p1 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 4))); |
| 200 | 199 | p2 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 8))); |
| 201 | 200 | p3 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 12))); |
| 202 | #else | |
| 203 | b = *src++ / 255.; | |
| 204 | g = *src++ / 255.; | |
| 205 | r = *src++ / 255.; | |
| 206 | #endif | |
| 207 | ||
| 208 | #ifdef __SSE4_1__ | |
| 201 | ||
| 209 | 202 | #define tint(v) \ |
| 210 | 203 | luma = _mm_dp_ps((v), weights, 0x7F); \ |
| 211 | 204 | v = _mm_add_ps(_mm_mul_ps(comp_amount, (v)), \ |
| 218 | 211 | p = _mm_packus_epi16(_mm_packus_epi32(p0, p1), |
| 219 | 212 | _mm_packus_epi32(p2, p3)); |
| 220 | 213 | |
| 221 | _mm_storeu_si128((__m128i*)outframe, p); | |
| 222 | ||
| 223 | /* Stride of 128 bits; i.e. 16 bytes */ | |
| 224 | inframe += 4; | |
| 225 | outframe += 4; | |
| 226 | #else | |
| 227 | luma = (b * .114 + g * .587 + r * .299); | |
| 228 | ||
| 229 | *dst++ = map_color(amount, comp_amount, b, luma, inst->blackColor.b, inst->whiteColor.b); | |
| 230 | *dst++ = map_color(amount, comp_amount, g, luma, inst->blackColor.g, inst->whiteColor.g); | |
| 231 | *dst++ = map_color(amount, comp_amount, r, luma, inst->blackColor.r, inst->whiteColor.r); | |
| 232 | *dst++ = *src++; | |
| 233 | #endif | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 214 | _mm_storeu_si128((__m128i*)(outframe + i * 4), p); | |
| 215 | } | |
| 216 | } | |
| 217 | #elif USE_AVX2 | |
| 218 | static void tint_avx2(const uint32_t* inframe, uint32_t* outframe, size_t len, | |
| 219 | double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) | |
| 220 | { | |
| 221 | // AVX2 implementation would go here | |
| 222 | // For now, fall back to scalar implementation | |
| 223 | // This is a placeholder for a future AVX2 implementation | |
| 224 | } | |
| 225 | #elif USE_NEON | |
| 226 | static void tint_neon(const uint32_t* inframe, uint32_t* outframe, size_t len, | |
| 227 | double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) | |
| 228 | { | |
| 229 | // NEON implementation would go here | |
| 230 | // For now, fall back to scalar implementation | |
| 231 | // This is a placeholder for a future NEON implementation | |
| 232 | } | |
| 233 | #endif | |
| 234 | ||
| 235 | static void tint_scalar(const uint32_t* inframe, uint32_t* outframe, size_t len, | |
| 236 | double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) | |
| 237 | { | |
| 238 | double comp_amount = 1.0 - amount; | |
| 239 | ||
| 240 | const unsigned char* src = (const unsigned char*)inframe; | |
| 241 | unsigned char* dst = (unsigned char*)outframe; | |
| 242 | ||
| 243 | float b, g, r; | |
| 244 | float luma; | |
| 245 | ||
| 246 | while (len--) | |
| 247 | { | |
| 248 | b = src[0] / 255.0f; | |
| 249 | g = src[1] / 255.0f; | |
| 250 | r = src[2] / 255.0f; | |
| 251 | ||
| 252 | luma = (b * 0.114f + g * 0.587f + r * 0.299f); | |
| 253 | ||
| 254 | dst[0] = map_color(amount, comp_amount, b, luma, blackColor.b, whiteColor.b); | |
| 255 | dst[1] = map_color(amount, comp_amount, g, luma, blackColor.g, whiteColor.g); | |
| 256 | dst[2] = map_color(amount, comp_amount, r, luma, blackColor.r, whiteColor.r); | |
| 257 | dst[3] = src[3]; // Copy alpha | |
| 258 | ||
| 259 | src += 4; | |
| 260 | dst += 4; | |
| 261 | } | |
| 262 | } | |
| 263 | ||
| 264 | void f0r_update(f0r_instance_t instance, double time, | |
| 265 | const uint32_t* inframe, uint32_t* outframe) | |
| 266 | { | |
| 267 | assert(instance); | |
| 268 | tint0r_instance_t* inst = (tint0r_instance_t*)instance; | |
| 269 | size_t len = inst->width * inst->height; | |
| 270 | ||
| 271 | #if USE_SSE4_1 | |
| 272 | // Process in chunks of 4 pixels for SSE | |
| 273 | size_t sse_len = len / 4; | |
| 274 | size_t remainder = len % 4; | |
| 275 | ||
| 276 | if (sse_len > 0) { | |
| 277 | tint_sse41(inframe, outframe, sse_len, inst->amount, inst->blackColor, inst->whiteColor); | |
| 278 | } | |
| 279 | ||
| 280 | // Handle remaining pixels with scalar implementation | |
| 281 | if (remainder > 0) { | |
| 282 | const uint32_t* remaining_in = inframe + (sse_len * 4); | |
| 283 | uint32_t* remaining_out = outframe + (sse_len * 4); | |
| 284 | tint_scalar(remaining_in, remaining_out, remainder, inst->amount, inst->blackColor, inst->whiteColor); | |
| 285 | } | |
| 286 | #else | |
| 287 | // Use scalar implementation for all pixels | |
| 288 | tint_scalar(inframe, outframe, len, inst->amount, inst->blackColor, inst->whiteColor); | |
| 289 | #endif | |
| 290 | } | |
| 291 | ||
| 0 | 0 | /* Water filter |
| 1 | 1 | * |
| 2 | * (c) Copyright 2000-2007 Denis Rojo <jaromil@dyne.org> | |
| 3 | * | |
| 2 | * (c) Copyright 2000-2025 Denis Roio <jaromil@dyne.org> | |
| 3 | * | |
| 4 | 4 | * from an original idea of water algorithm by Federico 'Pix' Feroldi |
| 5 | 5 | * |
| 6 | 6 | * this code contains optimizations by Jason Hood and Scott Scriven |
| 9 | 9 | * ported to C++ and frei0r plugin API in 2007 |
| 10 | 10 | * |
| 11 | 11 | * This source code is free software; you can redistribute it and/or |
| 12 | * modify it under the terms of the GNU Public License as published | |
| 12 | * modify it under the terms of the GNU Public License as published | |
| 13 | 13 | * by the Free Software Foundation; either version 2 of the License, |
| 14 | 14 | * or (at your option) any later version. |
| 15 | 15 | * |
| 21 | 21 | * You should have received a copy of the GNU Public License along with |
| 22 | 22 | * this source code; if not, write to: |
| 23 | 23 | * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 24 | * | |
| 25 | * "$Id: water.c 193 2004-06-01 11:00:25Z jaromil $" | |
| 26 | 24 | * |
| 27 | 25 | */ |
| 28 | 26 | |
| 80 | 78 | surfer = 0; |
| 81 | 79 | distort = 0; |
| 82 | 80 | smooth = 0; |
| 83 | position.x = 0; | |
| 84 | position.y = 0; | |
| 81 | position.x = 0.0; | |
| 82 | position.y = 0.0; | |
| 83 | //randomize_swirl = false; | |
| 85 | 84 | register_param(physics, "physics", "water density: from 0.0 to 1.0"); |
| 86 | 85 | register_param(swirl, "swirl", "swirling whirpool in the center"); |
| 87 | 86 | register_param(rain, "rain", "rain drops all over"); |
| 119 | 118 | |
| 120 | 119 | water_surfacesize = geo->size; |
| 121 | 120 | calc_optimization = (height)*(width); |
| 122 | ||
| 121 | ||
| 123 | 122 | xang = fastrand()%2048; |
| 124 | 123 | yang = fastrand()%2048; |
| 125 | 124 | swirlangle = fastrand()%2048; |
| 126 | ||
| 125 | ||
| 127 | 126 | /* buffer allocation tango */ |
| 128 | 127 | if ( width*height > 0 ) { |
| 129 | 128 | Height[0] = (uint32_t*)calloc(width*(height+1), sizeof(uint32_t)); |
| 134 | 133 | BkGdImage = (uint32_t*) malloc(geo->size); |
| 135 | 134 | BkGdImagePost = (uint32_t*)malloc(geo->size); |
| 136 | 135 | } |
| 136 | ||
| 137 | // Initialize surface to NULL | |
| 138 | surface = NULL; | |
| 137 | 139 | } |
| 138 | 140 | |
| 139 | 141 | ~Water() { |
| 151 | 153 | memcpy(BkGdImage, in, width*height*sizeof(uint32_t)); |
| 152 | 154 | water_update(out); |
| 153 | 155 | } |
| 154 | ||
| 156 | ||
| 155 | 157 | private: |
| 156 | 158 | ScreenGeometry *geo; |
| 157 | 159 | |
| 161 | 163 | uint32_t *BkGdImagePre; |
| 162 | 164 | uint32_t *BkGdImage; |
| 163 | 165 | uint32_t *BkGdImagePost; |
| 164 | ||
| 166 | ||
| 165 | 167 | // uint32_t *buffer; |
| 166 | ||
| 168 | ||
| 167 | 169 | void *surface; |
| 168 | ||
| 170 | ||
| 169 | 171 | /* water effect variables */ |
| 170 | 172 | int Hpage; |
| 171 | 173 | int xang, yang; |
| 173 | 175 | int x, y, ox, oy; |
| 174 | 176 | int done; |
| 175 | 177 | int mode; |
| 176 | ||
| 178 | ||
| 177 | 179 | /* precalculated to optimize a bit */ |
| 178 | 180 | int water_surfacesize; |
| 179 | 181 | int calc_optimization; |
| 180 | ||
| 182 | ||
| 181 | 183 | /* density: water density (step 1) |
| 182 | 184 | pheight: splash height (step 40) |
| 183 | 185 | radius: waterdrop radius (step 1) */ |
| 184 | 186 | int density, pheight, radius; |
| 185 | int offset; | |
| 186 | ||
| 187 | int offset; | |
| 188 | ||
| 187 | 189 | int raincount; |
| 188 | 190 | int blend; |
| 189 | 191 | |
| 190 | 192 | void water_clear(); |
| 191 | 193 | void water_distort(); |
| 192 | 194 | void water_setphysics(double physics); |
| 193 | void water_update(uint32_t* out); | |
| 195 | void water_update(uint32_t *out); | |
| 194 | 196 | void water_drop(int x, int y); |
| 195 | 197 | void water_bigsplash(int x, int y); |
| 196 | 198 | void water_surfer(); |
| 197 | 199 | void water_swirl(); |
| 198 | 200 | void water_3swirls(); |
| 199 | ||
| 200 | void DrawWater(int page,uint32_t* out); | |
| 201 | ||
| 202 | void DrawWater(int page, uint32_t* out); | |
| 201 | 203 | void CalcWater(int npage, int density); |
| 202 | 204 | void CalcWaterBigFilter(int npage, int density); |
| 203 | ||
| 205 | ||
| 204 | 206 | void SmoothWater(int npage); |
| 205 | ||
| 207 | ||
| 206 | 208 | void HeightBlob(int x, int y, int radius, int height, int page); |
| 207 | 209 | void HeightBox (int x, int y, int radius, int height, int page); |
| 208 | ||
| 210 | ||
| 209 | 211 | void WarpBlob(int x, int y, int radius, int height, int page); |
| 210 | 212 | void SineBlob(int x, int y, int radius, int height, int page); |
| 211 | 213 | |
| 214 | 216 | int FSin(int angle) { return FSinTab[angle&FSINMAX]; } |
| 215 | 217 | int FCos(int angle) { return FCosTab[angle&FSINMAX]; } |
| 216 | 218 | void FCreateSines() { |
| 217 | int i; double angle; | |
| 219 | int i; double angle; | |
| 218 | 220 | for(i=0; i<2048; i++) { |
| 219 | 221 | angle = (float)i * (PI/1024.0); |
| 220 | 222 | FSinTab[i] = (int)(sin(angle) * (float)0x10000); |
| 226 | 228 | uint32_t randval; |
| 227 | 229 | uint32_t fastrand() { return (randval=randval*1103515245+12345); }; |
| 228 | 230 | void fastsrand(uint32_t seed) { randval = seed; }; |
| 229 | ||
| 231 | ||
| 230 | 232 | /* integer optimized square root by jaromil */ |
| 231 | 233 | int isqrt(unsigned int x) { |
| 232 | 234 | unsigned int m, y, b; m = 0x40000000; |
| 234 | 236 | if(x>=b) { x=x-b; y=y|m; } |
| 235 | 237 | m=m>>2; } return y; |
| 236 | 238 | } |
| 237 | ||
| 239 | ||
| 238 | 240 | }; |
| 239 | 241 | |
| 240 | 242 | void Water::water_clear() { |
| 305 | 307 | } |
| 306 | 308 | |
| 307 | 309 | void Water::water_surfer() { |
| 310 | int x, y; | |
| 308 | 311 | x = (geo->w>>1) |
| 309 | 312 | + (( |
| 310 | 313 | ( |
| 321 | 324 | ) >> 16); |
| 322 | 325 | xang += 13; |
| 323 | 326 | yang += 12; |
| 324 | ||
| 327 | ||
| 325 | 328 | if(mode & 0x4000) |
| 326 | 329 | { |
| 327 | 330 | offset = (oy+y)/2*geo->w + ((ox+x)>>1); // QUAAA |
| 330 | 333 | Height[Hpage][offset - 1] = |
| 331 | 334 | Height[Hpage][offset + geo->w] = |
| 332 | 335 | Height[Hpage][offset - geo->w] = pheight >> 1; |
| 333 | ||
| 336 | ||
| 334 | 337 | offset = y*geo->w + x; |
| 335 | 338 | Height[Hpage][offset] = pheight<<1; |
| 336 | 339 | Height[Hpage][offset + 1] = |
| 343 | 346 | SineBlob(((ox+x)>>1), ((oy+y)>>1), 3, -1200, Hpage); |
| 344 | 347 | SineBlob(x, y, 4, -2000, Hpage); |
| 345 | 348 | } |
| 346 | ||
| 349 | ||
| 347 | 350 | ox = x; |
| 348 | oy = y; | |
| 351 | oy = y; | |
| 349 | 352 | } |
| 350 | 353 | |
| 351 | 354 | void Water::water_swirl() { |
| 355 | int x, y; | |
| 352 | 356 | x = (geo->w>>1) |
| 353 | 357 | + (( |
| 354 | 358 | (FCos(swirlangle)) * (25) |
| 355 | 359 | ) >> 16); |
| 356 | ||
| 360 | ||
| 357 | 361 | y = (geo->h>>1) |
| 358 | 362 | + (( |
| 359 | 363 | (FSin(swirlangle)) * (25) |
| 360 | 364 | ) >> 16); |
| 361 | x += position.x; | |
| 362 | y += position.y; | |
| 365 | x += (int)(position.x * geo->w); | |
| 366 | y += (int)(position.y * geo->h); | |
| 363 | 367 | |
| 364 | 368 | swirlangle += 50; |
| 365 | 369 | if(mode & 0x4000) |
| 370 | 374 | |
| 371 | 375 | void Water::water_3swirls() { |
| 372 | 376 | #define ANGLE 15 |
| 377 | int x, y; | |
| 373 | 378 | x = (95) |
| 374 | 379 | + (( |
| 375 | 380 | (FCos(swirlangle)) * (ANGLE) |
| 381 | 386 | |
| 382 | 387 | if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); |
| 383 | 388 | else WarpBlob(x, y, radius, pheight, Hpage); |
| 384 | ||
| 389 | ||
| 385 | 390 | x = (95) |
| 386 | 391 | + (( |
| 387 | 392 | (FCos(swirlangle)) * (ANGLE) |
| 390 | 395 | + (( |
| 391 | 396 | (FSin(swirlangle)) * (ANGLE) |
| 392 | 397 | ) >> 16); |
| 393 | ||
| 398 | ||
| 394 | 399 | if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); |
| 395 | 400 | else WarpBlob(x, y, radius, pheight, Hpage); |
| 396 | ||
| 401 | ||
| 397 | 402 | x = (345) |
| 398 | 403 | + (( |
| 399 | 404 | (FCos(swirlangle)) * (ANGLE) |
| 402 | 407 | + (( |
| 403 | 408 | (FSin(swirlangle)) * (ANGLE) |
| 404 | 409 | ) >> 16); |
| 405 | ||
| 410 | ||
| 406 | 411 | if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); |
| 407 | 412 | else WarpBlob(x, y, radius, pheight, Hpage); |
| 408 | 413 | |
| 412 | 417 | /* internal physics routines */ |
| 413 | 418 | void Water::DrawWater(int page,uint32_t* out) { |
| 414 | 419 | int dx, dy; |
| 415 | int x, y; | |
| 416 | uint32_t offset=geo->w + 1; | |
| 417 | uint32_t newoffset; | |
| 420 | int offset=geo->w + 1; | |
| 421 | int newoffset; | |
| 422 | int maxoffset = geo->w * geo->h; | |
| 418 | 423 | int *ptr = (int*)&Height[page][0]; |
| 419 | int maxoffset=geo->size/sizeof(uint32_t); | |
| 420 | ||
| 421 | for (y = calc_optimization; offset < y; offset+=2) { | |
| 422 | for (x = offset+geo->w-2; offset < x; offset++) { | |
| 423 | ||
| 424 | ||
| 425 | for (int y = calc_optimization; offset < y; offset += 2) { | |
| 426 | for (int x = offset+geo->w-2; offset < x; offset++) { | |
| 424 | 427 | dx = ptr[offset] - ptr[offset+1]; |
| 425 | 428 | dy = ptr[offset] - ptr[offset+geo->w]; |
| 426 | 429 | newoffset = offset + geo->w*(dy>>3) + (dx>>3); |
| 427 | if(newoffset<maxoffset) | |
| 430 | if (newoffset < maxoffset) { | |
| 428 | 431 | out[offset] = BkGdImage[newoffset]; |
| 432 | } | |
| 433 | ||
| 429 | 434 | offset++; |
| 430 | ||
| 431 | 435 | dx = ptr[offset] - ptr[offset+1]; |
| 432 | 436 | dy = ptr[offset] - ptr[offset+geo->w]; |
| 433 | 437 | newoffset = offset + geo->w*(dy>>3) + (dx>>3); |
| 434 | if(newoffset<maxoffset) | |
| 438 | if (newoffset < maxoffset) { | |
| 435 | 439 | out[offset] = BkGdImage[newoffset]; |
| 440 | } | |
| 436 | 441 | } |
| 437 | 442 | } |
| 438 | 443 | } |
| 442 | 447 | int count = geo->w + 1; |
| 443 | 448 | int *newptr = (int*) &Height[npage][0]; |
| 444 | 449 | int *oldptr = (int*) &Height[npage^1][0]; |
| 445 | int x, y; | |
| 446 | ||
| 447 | for (y = calc_optimization; count < y; count += 2) { | |
| 448 | for (x = count+geo->w-2; count < x; count++) { | |
| 450 | ||
| 451 | for (int y = calc_optimization; count < y; count += 2) { | |
| 452 | for (int x = count+geo->w-2; count < x; count++) { | |
| 449 | 453 | /* eight pixels */ |
| 450 | 454 | newh = ((oldptr[count + geo->w] |
| 451 | 455 | + oldptr[count - geo->w] |
| 467 | 471 | int count = geo->w + 1; |
| 468 | 472 | int *newptr = (int*) &Height[npage][0]; |
| 469 | 473 | int *oldptr = (int*) &Height[npage^1][0]; |
| 470 | int x, y; | |
| 471 | ||
| 472 | for(y=1; y<geo->h-1; y++) { | |
| 473 | for(x=1; x<geo->w-1; x++) { | |
| 474 | ||
| 475 | for(int y=1; y<geo->h-1; y++) { | |
| 476 | for(int x=1; x<geo->w-1; x++) { | |
| 474 | 477 | /* eight pixel */ |
| 475 | 478 | newh = ((oldptr[count + geo->w] |
| 476 | 479 | + oldptr[count - geo->w] |
| 482 | 485 | + oldptr[count + geo->w + 1] |
| 483 | 486 | ) >> 3 ) |
| 484 | 487 | + newptr[count]; |
| 485 | ||
| 486 | ||
| 488 | ||
| 489 | ||
| 487 | 490 | newptr[count] = newh>>1; |
| 488 | 491 | count++; |
| 489 | 492 | } |
| 496 | 499 | int count = (geo->w<<1) + 2; |
| 497 | 500 | int *newptr = (int*) &Height[npage][0]; |
| 498 | 501 | int *oldptr = (int*) &Height[npage^1][0]; |
| 499 | int x, y; | |
| 500 | ||
| 501 | for(y=2; y<geo->h-2; y++) { | |
| 502 | for(x=2; x<geo->w-2; x++) { | |
| 502 | ||
| 503 | for(int y=2; y<geo->h-2; y++) { | |
| 504 | for(int x=2; x<geo->w-2; x++) { | |
| 503 | 505 | /* 25 pixels */ |
| 504 | 506 | newh = ( |
| 505 | 507 | ( |
| 558 | 560 | for(cy = top; cy < bottom; cy++) { |
| 559 | 561 | cyq = cy*cy; |
| 560 | 562 | for(cx = left; cx < right; cx++) { |
| 561 | if(cx*cx + cyq < rquad) | |
| 562 | Height[page][geo->w*(cy+y) + (cx+x)] += height; | |
| 563 | if(cx*cx + cyq < rquad) { | |
| 564 | int index = geo->w*(cy+y) + (cx+x); | |
| 565 | // Bounds check | |
| 566 | if (index >= 0 && index < geo->w * geo->h) { | |
| 567 | Height[page][index] += height; | |
| 568 | } | |
| 569 | } | |
| 563 | 570 | } |
| 564 | 571 | } |
| 565 | 572 | } |
| 571 | 578 | |
| 572 | 579 | if(x<0) x = 1+radius+ fastrand()%(geo->w-2*radius-1); |
| 573 | 580 | if(y<0) y = 1+radius+ fastrand()%(geo->h-2*radius-1); |
| 574 | ||
| 581 | ||
| 575 | 582 | left=-radius; right = radius; |
| 576 | 583 | top=-radius; bottom = radius; |
| 577 | ||
| 584 | ||
| 578 | 585 | CLIP_EDGES |
| 579 | ||
| 586 | ||
| 580 | 587 | for(cy = top; cy < bottom; cy++) { |
| 581 | 588 | for(cx = left; cx < right; cx++) { |
| 582 | Height[page][geo->w*(cy+y) + (cx+x)] = height; | |
| 583 | } | |
| 584 | } | |
| 589 | int index = geo->w*(cy+y) + (cx+x); | |
| 590 | // Bounds check | |
| 591 | if (index >= 0 && index < geo->w * geo->h) { | |
| 592 | Height[page][index] = height; | |
| 593 | } | |
| 594 | } | |
| 595 | } | |
| 585 | 596 | } |
| 586 | 597 | |
| 587 | 598 | void Water::WarpBlob(int x, int y, int radius, int height, int page) { |
| 589 | 600 | int left,top,right,bottom; |
| 590 | 601 | int square; |
| 591 | 602 | int radsquare = radius * radius; |
| 592 | ||
| 603 | ||
| 593 | 604 | radsquare = (radius*radius); |
| 594 | ||
| 605 | ||
| 595 | 606 | height = height>>5; |
| 596 | ||
| 607 | ||
| 597 | 608 | left=-radius; right = radius; |
| 598 | 609 | top=-radius; bottom = radius; |
| 599 | 610 | |
| 600 | 611 | CLIP_EDGES |
| 601 | ||
| 612 | ||
| 602 | 613 | for(cy = top; cy < bottom; cy++) { |
| 603 | 614 | for(cx = left; cx < right; cx++) { |
| 604 | 615 | square = cy*cy + cx*cx; |
| 605 | 616 | if(square < radsquare) { |
| 606 | Height[page][geo->w*(cy+y) + cx+x] | |
| 607 | += (int)((radius-isqrt(square))*(float)(height)); | |
| 617 | int index = geo->w*(cy+y) + cx+x; | |
| 618 | // Bounds check | |
| 619 | if (index >= 0 && index < geo->w * geo->h) { | |
| 620 | Height[page][index] += (int)((radius-isqrt(square))*(float)(height)); | |
| 621 | } | |
| 608 | 622 | } |
| 609 | 623 | } |
| 610 | 624 | } |
| 616 | 630 | int square, dist; |
| 617 | 631 | int radsquare = radius * radius; |
| 618 | 632 | float length = (1024.0/(float)radius)*(1024.0/(float)radius); |
| 619 | ||
| 633 | ||
| 620 | 634 | if(x<0) x = 1+radius+ fastrand()%(geo->w-2*radius-1); |
| 621 | 635 | if(y<0) y = 1+radius+ fastrand()%(geo->h-2*radius-1); |
| 622 | 636 | |
| 631 | 645 | square = cy*cy + cx*cx; |
| 632 | 646 | if(square < radsquare) { |
| 633 | 647 | dist = (int)(isqrt(square*length)); |
| 634 | Height[page][geo->w*(cy+y) + cx+x] | |
| 635 | += (int)((FCos(dist)+0xffff)*(height)) >> 19; | |
| 648 | int index = geo->w*(cy+y) + cx+x; | |
| 649 | // Bounds check | |
| 650 | if (index >= 0 && index < geo->w * geo->h) { | |
| 651 | Height[page][index] += (int)((FCos(dist)+0xffff)*(height)) >> 19; | |
| 652 | } | |
| 636 | 653 | } |
| 637 | 654 | } |
| 638 | 655 | } |
| 28 | 28 | public: |
| 29 | 29 | blend(unsigned int width, unsigned int height) |
| 30 | 30 | { |
| 31 | this->width = width; | |
| 32 | this->height = height; | |
| 33 | this->size = width * height; | |
| 31 | 34 | blend_factor = 0.5; |
| 32 | 35 | register_param(blend_factor,"blend","blend factor"); |
| 33 | 36 | } |
| 44 | 47 | const uint32_t* in1, |
| 45 | 48 | const uint32_t* in2) |
| 46 | 49 | { |
| 50 | // Validate inputs | |
| 51 | if (!out || !in1 || !in2) { | |
| 52 | return; | |
| 53 | } | |
| 54 | ||
| 47 | 55 | const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1); |
| 48 | 56 | const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2); |
| 49 | 57 | uint8_t *dst = reinterpret_cast<uint8_t*>(out); |
| 51 | 59 | const uint8_t one_minus_bf = (255 - bf); |
| 52 | 60 | uint32_t w = size; |
| 53 | 61 | uint32_t b; |
| 54 | ||
| 62 | ||
| 63 | // Validate size | |
| 64 | if (w == 0) { | |
| 65 | return; | |
| 66 | } | |
| 67 | ||
| 55 | 68 | while (w--) |
| 56 | 69 | { |
| 57 | 70 | for (b = 0; b < NBYTES; b++) |
| 58 | 71 | dst[b] = (src1[b] * one_minus_bf + src2[b] * bf) / 255; |
| 59 | ||
| 72 | ||
| 60 | 73 | src1 += NBYTES; |
| 61 | 74 | src2 += NBYTES; |
| 62 | 75 | dst += NBYTES; |
| 76 | 76 | |
| 77 | 77 | f0r_instance_t f0r_construct(unsigned int width, unsigned int height) |
| 78 | 78 | { |
| 79 | // Validate inputs | |
| 80 | if (width == 0 || height == 0) { | |
| 81 | return NULL; | |
| 82 | } | |
| 83 | ||
| 79 | 84 | cairo_blend_instance_t* inst = (cairo_blend_instance_t*)calloc(1, sizeof(*inst)); |
| 80 | inst->width = width; | |
| 85 | if (!inst) { | |
| 86 | return NULL; | |
| 87 | } | |
| 88 | ||
| 89 | inst->width = width; | |
| 81 | 90 | inst->height = height; |
| 82 | 91 | |
| 83 | 92 | inst->opacity = 1.0; |
| 84 | 93 | |
| 85 | const char* blend_val = NORMAL; | |
| 86 | inst->blend_mode = (char*) malloc (strlen(blend_val) + 1 ); | |
| 87 | strcpy (inst->blend_mode, blend_val); | |
| 94 | const char* blend_val = NORMAL; | |
| 95 | inst->blend_mode = (char*) malloc (strlen(blend_val) + 1); | |
| 96 | if (!inst->blend_mode) { | |
| 97 | free(inst); | |
| 98 | return NULL; | |
| 99 | } | |
| 100 | strcpy (inst->blend_mode, blend_val); | |
| 88 | 101 | |
| 89 | 102 | return (f0r_instance_t)inst; |
| 90 | 103 | } |
| 91 | 104 | |
| 92 | 105 | void f0r_destruct(f0r_instance_t instance) |
| 93 | 106 | { |
| 107 | if (!instance) { | |
| 108 | return; | |
| 109 | } | |
| 110 | ||
| 94 | 111 | cairo_blend_instance_t* inst = (cairo_blend_instance_t*)instance; |
| 95 | free(inst->blend_mode); | |
| 112 | if (inst->blend_mode) { | |
| 113 | free(inst->blend_mode); | |
| 114 | } | |
| 96 | 115 | free(instance); |
| 97 | 116 | } |
| 98 | 117 | |
| 103 | 122 | char* sval; |
| 104 | 123 | switch(param_index) { |
| 105 | 124 | case 0: |
| 106 | inst->opacity = *((double*)param); | |
| 125 | // Validate double parameter | |
| 126 | if (param) { | |
| 127 | inst->opacity = *((double*)param); | |
| 128 | } | |
| 107 | 129 | break; |
| 108 | 130 | case 1: |
| 109 | sval = (*(char**)param); | |
| 110 | inst->blend_mode = (char*)realloc (inst->blend_mode, strlen(sval) + 1); | |
| 111 | strcpy (inst->blend_mode, sval); | |
| 131 | // Validate string parameter | |
| 132 | if (param) { | |
| 133 | sval = (*(char**)param); | |
| 134 | if (sval) { | |
| 135 | inst->blend_mode = (char*)realloc (inst->blend_mode, strlen(sval) + 1); | |
| 136 | if (inst->blend_mode) { | |
| 137 | strcpy (inst->blend_mode, sval); | |
| 138 | } | |
| 139 | } | |
| 140 | } | |
| 112 | 141 | break; |
| 113 | 142 | } |
| 114 | 143 | } |
| 120 | 149 | |
| 121 | 150 | switch(param_index) { |
| 122 | 151 | case 0: |
| 123 | *((double*)param) = inst->opacity; | |
| 152 | if (param) { | |
| 153 | *((double*)param) = inst->opacity; | |
| 154 | } | |
| 124 | 155 | break; |
| 125 | 156 | case 1: |
| 126 | *((f0r_param_string *)param) = inst->blend_mode; | |
| 157 | if (param && inst->blend_mode) { | |
| 158 | *((f0r_param_string *)param) = inst->blend_mode; | |
| 159 | } else if (param) { | |
| 160 | *((f0r_param_string *)param) = ""; | |
| 161 | } | |
| 127 | 162 | break; |
| 128 | 163 | } |
| 129 | 164 | } |
| 133 | 168 | int w = inst->width; |
| 134 | 169 | int h = inst->height; |
| 135 | 170 | int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w); |
| 171 | ||
| 172 | // Validate inputs | |
| 173 | if (!inst || !out || !src || w <= 0 || h <= 0) { | |
| 174 | return; | |
| 175 | } | |
| 136 | 176 | |
| 137 | 177 | cairo_surface_t* out_image = cairo_image_surface_create_for_data (out, |
| 138 | 178 | CAIRO_FORMAT_ARGB32, |
| 139 | 179 | w, |
| 140 | 180 | h, |
| 141 | 181 | stride); |
| 182 | // Check if surface creation succeeded | |
| 183 | if (!out_image) { | |
| 184 | return; | |
| 185 | } | |
| 186 | ||
| 142 | 187 | cairo_t* cr = cairo_create (out_image); |
| 188 | // Check if context creation succeeded | |
| 189 | if (!cr) { | |
| 190 | cairo_surface_destroy (out_image); | |
| 191 | return; | |
| 192 | } | |
| 143 | 193 | |
| 144 | 194 | cairo_surface_t* src_image = cairo_image_surface_create_for_data ((unsigned char*)src, |
| 145 | 195 | CAIRO_FORMAT_ARGB32, |
| 146 | 196 | w, |
| 147 | 197 | h, |
| 148 | 198 | stride); |
| 149 | ||
| 150 | // Set source, blen mode and draw with current opacity | |
| 151 | frei0r_cairo_set_operator(cr, inst->blend_mode); | |
| 199 | // Check if surface creation succeeded | |
| 200 | if (!src_image) { | |
| 201 | cairo_destroy (cr); | |
| 202 | cairo_surface_destroy (out_image); | |
| 203 | return; | |
| 204 | } | |
| 205 | ||
| 206 | // Validate blend mode string | |
| 207 | if (inst->blend_mode) { | |
| 208 | // Set source, blend mode and draw with current opacity | |
| 209 | frei0r_cairo_set_operator(cr, inst->blend_mode); | |
| 210 | } | |
| 211 | ||
| 152 | 212 | cairo_set_source_surface (cr, src_image, 0, 0); |
| 153 | 213 | cairo_paint_with_alpha (cr, inst->opacity); |
| 154 | 214 | |
| 155 | cairo_surface_destroy (out_image); | |
| 215 | // Clean up in proper order | |
| 156 | 216 | cairo_surface_destroy (src_image); |
| 157 | 217 | cairo_destroy (cr); |
| 218 | cairo_surface_destroy (out_image); | |
| 158 | 219 | } |
| 159 | 220 | |
| 160 | 221 | void f0r_update(f0r_instance_t instance, double time, |
| 166 | 227 | void f0r_update2(f0r_instance_t instance, double time, const uint32_t* inframe1, |
| 167 | 228 | const uint32_t* inframe2, const uint32_t* inframe3, uint32_t* outframe) |
| 168 | 229 | { |
| 230 | // Validate inputs | |
| 231 | if (!instance || !inframe1 || !inframe2 || !outframe) { | |
| 232 | return; | |
| 233 | } | |
| 234 | ||
| 169 | 235 | assert(instance); |
| 170 | 236 | cairo_blend_instance_t* inst = (cairo_blend_instance_t*) instance; |
| 171 | 237 | |
| 174 | 240 | unsigned char* out = (unsigned char*)outframe; |
| 175 | 241 | int pixels = inst->width * inst->height; |
| 176 | 242 | |
| 243 | // Validate dimensions | |
| 244 | if (pixels <= 0) { | |
| 245 | return; | |
| 246 | } | |
| 247 | ||
| 177 | 248 | frei0r_cairo_premultiply_rgba2 (dst, out, pixels, -1); |
| 178 | 249 | frei0r_cairo_premultiply_rgba (src, pixels, -1); |
| 179 | 250 | draw_composite (inst, out, src, time); |
| 28 | 28 | public: |
| 29 | 29 | overlay(unsigned int width, unsigned int height) |
| 30 | 30 | { |
| 31 | this->width = width; | |
| 32 | this->height = height; | |
| 33 | this->size = width * height; | |
| 31 | 34 | } |
| 32 | 35 | |
| 33 | 36 | /** |
| 43 | 46 | const uint32_t* in1, |
| 44 | 47 | const uint32_t* in2) |
| 45 | 48 | { |
| 49 | // Validate inputs | |
| 50 | if (!out || !in1 || !in2) { | |
| 51 | return; | |
| 52 | } | |
| 53 | ||
| 46 | 54 | const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1); |
| 47 | 55 | const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2); |
| 48 | 56 | uint8_t *dst = reinterpret_cast<uint8_t*>(out); |
| 49 | 57 | uint32_t sizeCounter = size; |
| 50 | ||
| 58 | ||
| 59 | // Validate size | |
| 60 | if (sizeCounter == 0) { | |
| 61 | return; | |
| 62 | } | |
| 63 | ||
| 51 | 64 | uint32_t b, tmp, tmpM; |
| 52 | ||
| 65 | ||
| 53 | 66 | while (sizeCounter--) |
| 54 | 67 | { |
| 55 | 68 | for (b = 0; b < ALPHA; b++) |
| 56 | 69 | { |
| 57 | 70 | dst[b] = INT_MULT(src1[b], src1[b] + INT_MULT(2 * src2[b], 255 - src1[b], tmpM), tmp); |
| 58 | 71 | } |
| 59 | ||
| 72 | ||
| 60 | 73 | dst[ALPHA] = MIN(src1[ALPHA], src2[ALPHA]); |
| 61 | ||
| 74 | ||
| 62 | 75 | src1 += NBYTES; |
| 63 | 76 | src2 += NBYTES; |
| 64 | 77 | dst += NBYTES; |