diff --git a/BUILD.md b/BUILD.md index 95cec0a..07388a6 100644 --- a/BUILD.md +++ b/BUILD.md @@ -10,11 +10,22 @@ + [Cairo](http://cairographics.org) required for cairo- filters and mixers +## Optional build flags + + + `-DWITHOUT_FACERECOGNITION=ON` - Disable face recognition plugins (facedetect and facebl0r) to avoid protobuf conflicts with applications like MLT + It is recommended to use a separate `build` sub-folder. ``` mkdir -p build cd build && cmake ../ +make +``` + +To disable face recognition plugins (recommended when using with MLT): +``` +mkdir -p build +cd build && cmake -DWITHOUT_FACERECOGNITION=ON ../ make ``` diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e25222..9bb5b8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,15 @@ -cmake_minimum_required (VERSION 3.12...3.31) +cmake_minimum_required (VERSION 3.12) list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) project (frei0r) -set (VERSION 1.8) +set (VERSION 2.5.1) include(GNUInstallDirs) option (WITHOUT_OPENCV "Disable plugins dependent upon OpenCV" OFF) +option (WITHOUT_FACERECOGNITION "Disable facedetect plugin to avoid protobuf conflicts" OFF) + if (NOT WITHOUT_OPENCV) find_package (OpenCV) endif () diff --git a/README.md b/README.md index 018f659..b6d42f6 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,10 @@ ## https://github.com/dyne/frei0r/releases Frei0r sourcecode is released under the terms of the GNU General Public License and, eventually other compatible Free Software licenses. + +## Packaging + +[![Packaging status](https://repology.org/badge/vertical-allrepos/frei0r.svg?columns=3)](https://repology.org/project/frei0r/versions) ## Build dependencies diff --git a/include/frei0r/cairo.h b/include/frei0r/cairo.h index 8aa2d88..5553616 100644 --- a/include/frei0r/cairo.h +++ b/include/frei0r/cairo.h @@ -62,6 +62,14 @@ */ void frei0r_cairo_set_operator(cairo_t *cr, char *op) { + // Validate inputs + if (!cr || !op) { + if (cr) { + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + } + return; + } + if(strcmp(op, NORMAL) == 0) { cairo_set_operator (cr, CAIRO_OPERATOR_OVER); @@ -231,6 +239,11 @@ */ void frei0r_cairo_premultiply_rgba (unsigned char *rgba, int pixels, int alpha) { + // Validate inputs + if (!rgba || pixels <= 0) { + return; + } + int i = pixels + 1; while ( --i ) { register unsigned char a = rgba[3]; @@ -255,6 +268,11 @@ */ void frei0r_cairo_unpremultiply_rgba (unsigned char *rgba, int pixels) { + // Validate inputs + if (!rgba || pixels <= 0) { + return; + } + int i = pixels + 1; while ( --i ) { register unsigned char a = rgba[3]; @@ -281,6 +299,11 @@ void frei0r_cairo_premultiply_rgba2 (unsigned char *in, unsigned char *out, int pixels, int alpha) { + // Validate inputs + if (!in || !out || pixels <= 0) { + return; + } + int i = pixels + 1; while ( --i ) { register unsigned char a = in[3]; diff --git a/src/filter/CMakeLists.txt b/src/filter/CMakeLists.txt index ee19ac6..6d52964 100644 --- a/src/filter/CMakeLists.txt +++ b/src/filter/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory (3dflippo) add_subdirectory (aech0r) add_subdirectory (alpha0ps) +add_subdirectory (autothresh0ld) add_subdirectory (balanc0r) add_subdirectory (baltan) add_subdirectory (bluescreen0r) diff --git a/src/filter/autothresh0ld/CMakeLists.txt b/src/filter/autothresh0ld/CMakeLists.txt new file mode 100644 index 0000000..a9fa70b --- /dev/null +++ b/src/filter/autothresh0ld/CMakeLists.txt @@ -0,0 +1,11 @@ +set (SOURCES autothresh0ld.c) +set (TARGET autothresh0ld) + +if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_DEF}) +endif (MSVC) + +add_library (${TARGET} MODULE ${SOURCES}) + +# No «lib» prefix (name.so instead of libname.so) +set_target_properties (${TARGET} PROPERTIES PREFIX "") diff --git a/src/filter/autothresh0ld/autothresh0ld.c b/src/filter/autothresh0ld/autothresh0ld.c new file mode 100644 index 0000000..4941d12 --- /dev/null +++ b/src/filter/autothresh0ld/autothresh0ld.c @@ -0,0 +1,192 @@ +/** + * (c) Copyright 2025 Cynthia + * + * This is based on Otsu's algorithm to segment an image into foreground + * or background based on the shape of the histogram. Instead of doing a + * hard threshold, we use the threshold obtained from Otsu's algorithm + * as the base for a sigmoidal transfer with a high slope; this produces + * a more eye-soothing threshold effect as seen in ImageMagick. + * + * This has the added benefit that, whereas the sigmoidal filter's base + * requires manual tuning, here it is determined algorithmically and thus + * can adapt on a frame-to-frame basis. + * + * This file is a Frei0r plugin. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "frei0r.h" +#include "frei0r/math.h" + +#include "variance.h" + +typedef struct { + unsigned int width, height; + float slope; + + uint8_t lut[256][256]; + uint8_t *lumaframe; +} s0ft0tsu_t; + +static void gen_sigmoid_lut (uint8_t lut[256][256], float slope) +{ + float k = expf(slope) / 255.0; + + for (int j = 0; j < 256; ++j) + for (int i = 0; i < 256; ++i) + lut[j][i] = CLAMP (255.0 / (1.0 + expf(-k * (i - j))), 0, 255); +} + +int f0r_init() +{ + return 0; +} + +void f0r_deinit() {} + +f0r_instance_t f0r_construct(unsigned int width, unsigned int height) +{ + s0ft0tsu_t* s = calloc(1, sizeof(s0ft0tsu_t)); + + s->width = width; + s->height = height; + s->slope = 4.0; + s->lumaframe = malloc(s->width * s->height); + + gen_sigmoid_lut(s->lut, s->slope); + + return s; +} + +void f0r_get_plugin_info(f0r_plugin_info_t* info) +{ + info->name = "autothreshold"; + info->author = "Cynthia"; + info->explanation = "Automatically threshold moving pictures"; + info->major_version = 0; + info->minor_version = 1; + info->frei0r_version = FREI0R_MAJOR_VERSION; + info->plugin_type = F0R_PLUGIN_TYPE_FILTER; + info->color_model = F0R_COLOR_MODEL_RGBA8888; + info->num_params = 1; +} + +void f0r_get_param_info(f0r_param_info_t *info, int index) +{ + switch (index) + { + case 0: + info->name = "Slope"; + info->explanation = "Slope of sigmoidal transfer"; + info->type = F0R_PARAM_DOUBLE; + break; + } +} + +void f0r_get_param_value(f0r_instance_t inst, f0r_param_t param, int index) +{ + s0ft0tsu_t* s = inst; + + switch (index) + { + case 0: + *(double*)param = s->slope / 7.0; + break; + } +} + +void f0r_set_param_value(f0r_instance_t inst, f0r_param_t param, int index) +{ + s0ft0tsu_t* s = inst; + + switch (index) + { + case 0: + s->slope = *(double*)param * 7.0; + + gen_sigmoid_lut(s->lut, s->slope); + break; + } +} + +void f0r_update(f0r_instance_t inst, double time, + const uint32_t* inframe, uint32_t* outframe) +{ + s0ft0tsu_t *s = inst; + + uint8_t *src = (uint8_t*)inframe; + uint8_t *dst = s->lumaframe; + uint8_t r, g, b, luma; + size_t len = s->width * s->height; + + // Normalised histogram (L = 256) + float hist[256] = {0}; + + for (int i = 0; i < len; ++i) + { + r = *src++; + g = *src++; + b = *src++; + src++; // Ignore alpha + + luma = CLAMP(0.299 * r + 0.587 * g + 0.114 * b, 0, 255); + *dst++ = luma; + + ++hist[luma]; // Add to histogram + } + + // Normalise histogram; becomes a probability distribution + for (int i = 0; i < 256; ++i) + hist[i] /= len; + + uint8_t thresh = 0.0; + float max_var = 0.0; // Maximum inter-class variance. + + for (int i = 1; i < 256; ++i) + { + float var = find_variance(hist, i); + if (var > max_var) + { + thresh = i; + max_var = var; + } + } + + src = (uint8_t*)s->lumaframe; // Replenish + dst = (uint8_t*)outframe; + + for (int i = 0; i < len; ++i) + { + // Select the appropriate transfer + uint8_t* lut = s->lut[thresh]; + + luma = lut[*src++]; + + *dst++ = luma; + *dst++ = luma; + *dst++ = luma; + *dst++ = 0xFF; // TODO Copy alpha + } +} + +void f0r_destruct(f0r_instance_t s) +{ + free(s); +} diff --git a/src/filter/autothresh0ld/variance.h b/src/filter/autothresh0ld/variance.h new file mode 100644 index 0000000..9e277aa --- /dev/null +++ b/src/filter/autothresh0ld/variance.h @@ -0,0 +1,84 @@ +#ifdef __SSE2__ +#include +#endif + +static float find_variance(float *hist, int thresh) +{ + float w_0 = 0.0, mu_0 = 0.0, w_1 = 0.0, mu_1 = 0.0; + + for (int i = 0; i < thresh; ++i) + { + w_0 += hist[i]; + mu_0 += i * hist[i]; + } + for (int i = thresh; i < 256; ++i) + { + w_1 += hist[i]; + mu_1 += i * hist[i]; + } + + float mu_diff = (mu_0/w_0 - mu_1/w_1); + return w_0*w_1*mu_diff*mu_diff; +} + +#ifdef __SSE2__ +static float _sse1_hadd_ps(__m128 v) +{ + // Based on a StackOverflow answer. + __m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1)); + __m128 sums = _mm_add_ps(v, shuf); + shuf = _mm_movehl_ps(shuf, sums); + sums = _mm_add_ss(sums, shuf); + return _mm_cvtss_f32(sums); +} + +static float find_variance_sse2(float *hist, int thresh) +{ + __m128 vw_0 = _mm_setzero_ps(), vmu_0 = _mm_setzero_ps(), + vw_1 = _mm_setzero_ps(), vmu_1 = _mm_setzero_ps(); + __m128 vcnt = _mm_set_ps(0, 1, 2, 3); + + int thresh_low = (thresh / 4 + 0) * 4, + thresh_up = (thresh / 4 + 1) * 4; + + for (int i = 0; i < thresh_low; i+=4) + { + __m128 vhist = _mm_castsi128_ps(_mm_loadu_si128((void*)&hist[i])); + + vw_0 = _mm_add_ps(vhist, vw_0); + vmu_0 = _mm_add_ps(_mm_mul_ps(vcnt, vhist), vw_0); + vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); + } + + // This is skipped, and handled as an edge case by non-SSE code. + vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); + + for (int i = thresh_up; i < 256; i+=4) + { + __m128 vhist = _mm_castsi128_ps(_mm_loadu_si128((void*)&hist[i])); + + vw_1 = _mm_add_ps(vhist, vw_1); + vmu_1 = _mm_add_ps(_mm_mul_ps(vcnt, vhist), vw_1); + vcnt = _mm_add_ps(vcnt, _mm_set1_ps(4)); + } + + float w_0 = _sse1_hadd_ps(vw_0), mu_0 = _sse1_hadd_ps(vmu_0), + w_1 = _sse1_hadd_ps(vw_1), mu_1 = _sse1_hadd_ps(vmu_1); + + // This edge case is here, because thresh may not be a multiple of 4. + for (int i = thresh_low; i < thresh; ++i) + { + w_0 += hist[i]; + mu_0 += i * hist[i]; + } + + for (int i = thresh; i < thresh_up; ++i) + { + w_1 += hist[i]; + mu_1 += i * hist[i]; + } + + float mu_diff = (mu_0/w_0 - mu_1/w_1); + return w_0*w_1*mu_diff*mu_diff; +} +#endif diff --git a/src/filter/bluescreen0r/bluescreen0r.cpp b/src/filter/bluescreen0r/bluescreen0r.cpp index 923f10b..0d83f49 100644 --- a/src/filter/bluescreen0r/bluescreen0r.cpp +++ b/src/filter/bluescreen0r/bluescreen0r.cpp @@ -83,7 +83,7 @@ *outpixel= (*pixel & 0x00FFFFFF); // copy all except alpha uint32_t d = distance(*pixel); // get distance - unsigned char a = 255; // default alpha + unsigned char a = (*pixel >> 24); // default alpha if (d < distInt) { a = 0; if (d > distInt2) { @@ -100,5 +100,8 @@ }; -frei0r::construct plugin("bluescreen0r", "Color to alpha (blit SRCALPHA)", "Hedde Bosman",0,4,F0R_COLOR_MODEL_RGBA8888); - +frei0r::construct plugin("bluescreen0r", + "Color to alpha (blit SRCALPHA)", + "Hedde Bosman", + 0, 5, + F0R_COLOR_MODEL_RGBA8888); diff --git a/src/filter/blur/IIRblur.c b/src/filter/blur/IIRblur.c index 4eb9055..2eb02f0 100644 --- a/src/filter/blur/IIRblur.c +++ b/src/filter/blur/IIRblur.c @@ -80,12 +80,13 @@ { float p[10]; int i,j,m; - float zero = 0.0f; // MSVC doesn't allow division through a zero literal, but allows it through non-const variable set to zero if ((xxt[t-1])) { // printf("\n\n x=%f je izven mej tabele!",x); - return 1.0/zero; + // Return a reasonable value instead of dividing by zero + if (x=j;i--) { - p[i]=p[i]+(x-xt[i+m])/(xt[i+m]-xt[i-j+m])*(p[i]-p[i-1]); + // Check for division by zero + float denominator = xt[i+m]-xt[i-j+m]; + if (denominator == 0.0f) { + // If denominator is zero, skip this iteration to avoid undefined behavior + continue; + } + p[i]=p[i]+(x-xt[i+m])/denominator*(p[i]-p[i-1]); } return p[4-1]; } diff --git a/src/filter/cartoon/cartoon.cpp b/src/filter/cartoon/cartoon.cpp index 1c9ddd9..14dcfc7 100644 --- a/src/filter/cartoon/cartoon.cpp +++ b/src/filter/cartoon/cartoon.cpp @@ -46,7 +46,7 @@ uint32_t size; } ScreenGeometry; -#define PIXELAT(x1,y1,s) ((s)+(x1)+ yprecal[y1])// (y1)*(geo->w))) +#define PIXELAT(x1,y1,s,inst) ((s)+(x1)+ inst->yprecal[y1])// (y1)*(geo->w))) #define GMERROR(cc1,cc2) ((((RED(cc1)-RED(cc2))*(RED(cc1)-RED(cc2))) + \ ((GREEN(cc1)-GREEN(cc2)) *(GREEN(cc1)-GREEN(cc2))) + \ ((BLUE(cc1)-BLUE(cc2))*(BLUE(cc1)-BLUE(cc2))))) @@ -155,26 +155,26 @@ long error,max=0; /* Assumes PrePixelModify has been run */ - c1 = *PIXELAT(x-m_diffspace,y,src); - c2 = *PIXELAT(x+m_diffspace,y,src); + c1 = *PIXELAT(x-m_diffspace,y,src,this); + c2 = *PIXELAT(x+m_diffspace,y,src,this); error = GMERROR(c1,c2); if (error>max) max = error; - - c1 = *PIXELAT(x,y-m_diffspace,src); - c2 = *PIXELAT(x,y+m_diffspace,src); + + c1 = *PIXELAT(x,y-m_diffspace,src,this); + c2 = *PIXELAT(x,y+m_diffspace,src,this); error = GMERROR(c1,c2); if (error>max) max = error; - - c1 = *PIXELAT(x-m_diffspace,y-m_diffspace,src); - c2 = *PIXELAT(x+m_diffspace,y+m_diffspace,src); + + c1 = *PIXELAT(x-m_diffspace,y-m_diffspace,src,this); + c2 = *PIXELAT(x+m_diffspace,y+m_diffspace,src,this); error = GMERROR(c1,c2); if (error>max) max = error; - - c1 = *PIXELAT(x+m_diffspace,y-m_diffspace,src); - c2 = *PIXELAT(x-m_diffspace,y+m_diffspace,src); + + c1 = *PIXELAT(x+m_diffspace,y-m_diffspace,src,this); + c2 = *PIXELAT(x-m_diffspace,y+m_diffspace,src,this); error = GMERROR(c1,c2); if (error>max) max = error; - + return(max); } diff --git a/src/filter/colordistance/colordistance.c b/src/filter/colordistance/colordistance.c index 51866b4..9385411 100644 --- a/src/filter/colordistance/colordistance.c +++ b/src/filter/colordistance/colordistance.c @@ -125,16 +125,17 @@ float b1 = inst->color.b * 255.0; float r2, g2, b2; int l; + /* Scale factor to normalize distance to 0-255 range: + 0.705724361914764 ≈ 255.0 / sqrt(3 * 255^2) = 255.0 / (255.0 * sqrt(3)) */ + const float SCALE_FACTOR = 0.705724361914764; while (len--) { r2 = *src++; g2 = *src++; b2 = *src++; - l = (int)rint( sqrtf( powf( r1 - r2, 2 ) + powf( g1 - g2, 2 ) + powf( b1 - b2, 2 ) ) * 0.705724361914764 ); - /* Hint 0.35320727852735 == 255.0 / sqrt( (255)**2 + (255)**2 + (255)*2 )*/ - 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 ) { - printf ("%f %f %f\n", r2, g2, b2 ); - } + l = (int)rint( sqrtf( powf( r1 - r2, 2 ) + powf( g1 - g2, 2 ) + powf( b1 - b2, 2 ) ) * SCALE_FACTOR ); + // Clamp result to valid range + l = CLAMP(l, 0, 255); *dst++ = (unsigned char) (l); *dst++ = (unsigned char) (l); diff --git a/src/filter/distort0r/distort0r.c b/src/filter/distort0r/distort0r.c index 8f597fc..a9ef908 100644 --- a/src/filter/distort0r/distort0r.c +++ b/src/filter/distort0r/distort0r.c @@ -254,16 +254,21 @@ step_line_u = (int32_t) ((u_right-u_left) >> GRID_SIZE_LOG); step_line_v = (int32_t) ((v_right-v_left) >> GRID_SIZE_LOG); - for(block_x=0; block_x < GRID_SIZE; ++block_x) - { + for(block_x=0; block_x < GRID_SIZE; ++block_x) + { int uu = u_line_index >> 16; int vv = v_line_index >> 16; - - u_line_index += step_line_u; - v_line_index += step_line_v; - - *pos++ = src[uu + vv * w]; - } + + u_line_index += step_line_u; + v_line_index += step_line_v; + + // Bounds checking to prevent buffer overrun + if (uu >= 0 && uu < (int)w && vv >= 0 && vv < (int)h) { + *pos++ = src[uu + vv * w]; + } else { + *pos++ = 0; // Black pixel for out-of-bounds access + } + } start_col_uu += step_start_col_u; end_col_uu += step_end_col_u; diff --git a/src/filter/facebl0r/CMakeLists.txt b/src/filter/facebl0r/CMakeLists.txt index 0cba89d..ca05a2a 100644 --- a/src/filter/facebl0r/CMakeLists.txt +++ b/src/filter/facebl0r/CMakeLists.txt @@ -1,13 +1,19 @@ -set (SOURCES facebl0r.cpp) -set (TARGET facebl0r) +# facebl0r filter - requires OpenCV (and protobuf) +# This filter can cause conflicts with applications that also use protobuf +# such as MLT, so it can be disabled with -DWITHOUT_FACERECOGNITION=ON -if (MSVC) - set (SOURCES ${SOURCES} ${FREI0R_DEF}) -endif (MSVC) +if (OpenCV_FOUND AND NOT WITHOUT_FACERECOGNITION) + set (SOURCES facebl0r.cpp) + set (TARGET facebl0r) -include_directories(${OpenCV_INCLUDE_DIRS}) -add_library (${TARGET} MODULE ${SOURCES}) -set_target_properties (${TARGET} PROPERTIES PREFIX "") -target_link_libraries(${TARGET} ${OpenCV_LIBS}) + if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_DEF}) + endif (MSVC) -install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) + add_library (${TARGET} MODULE ${SOURCES}) + set_target_properties (${TARGET} PROPERTIES PREFIX "") + target_link_libraries(${TARGET} ${OpenCV_LIBS}) + + install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) +endif() diff --git a/src/filter/facedetect/CMakeLists.txt b/src/filter/facedetect/CMakeLists.txt index b9ea1cd..ea17fcb 100644 --- a/src/filter/facedetect/CMakeLists.txt +++ b/src/filter/facedetect/CMakeLists.txt @@ -1,13 +1,19 @@ -set (SOURCES facedetect.cpp) -set (TARGET facedetect) +# facedetect filter - requires OpenCV (and protobuf) +# This filter can cause conflicts with applications that also use protobuf +# such as MLT, so it can be disabled with -DWITHOUT_FACERECOGNITION=ON -if (MSVC) - set (SOURCES ${SOURCES} ${FREI0R_DEF}) -endif (MSVC) +if (OpenCV_FOUND AND NOT WITHOUT_FACERECOGNITION) + set (SOURCES facedetect.cpp) + set (TARGET facedetect) -include_directories(${OpenCV_INCLUDE_DIRS}) -add_library (${TARGET} MODULE ${SOURCES}) -set_target_properties (${TARGET} PROPERTIES PREFIX "") -target_link_libraries(${TARGET} ${OpenCV_LIBS}) + if (MSVC) + set (SOURCES ${SOURCES} ${FREI0R_DEF}) + endif (MSVC) -install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) + include_directories(${OpenCV_INCLUDE_DIRS}) + add_library (${TARGET} MODULE ${SOURCES}) + set_target_properties (${TARGET} PROPERTIES PREFIX "") + target_link_libraries(${TARGET} ${OpenCV_LIBS}) + + install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) +endif() diff --git a/src/filter/glitch0r/glitch0r.c b/src/filter/glitch0r/glitch0r.c index 34264d0..ef462cb 100644 --- a/src/filter/glitch0r/glitch0r.c +++ b/src/filter/glitch0r/glitch0r.c @@ -45,7 +45,7 @@ short int howToDistort1; short int howToDistort2; short int passThisLine; -} g0r_state; +}; typedef struct glitch0r_instance { @@ -58,6 +58,8 @@ short int colorGlitchIntensity; short int doColorDistortion; short int glitchChance; + + struct glitch0r_state state; // Instance-specific state } glitch0r_instance_t; @@ -68,17 +70,17 @@ inline static void glitch0r_state_reset(glitch0r_instance_t *inst) { - g0r_state.currentPos = 0; - g0r_state.currentBlock = rnd(1, inst->maxBlockSize); - g0r_state.blkShift = rnd(1, inst->maxBlockShift); - g0r_state.passThisLine = (inst->glitchChance < rnd(1, 101)) ? 1 : 0; + inst->state.currentPos = 0; + inst->state.currentBlock = rnd(1, inst->maxBlockSize); + inst->state.blkShift = rnd(1, inst->maxBlockShift); + inst->state.passThisLine = (inst->glitchChance < rnd(1, 101)) ? 1 : 0; if (inst->doColorDistortion) { - g0r_state.distortionSeed1 = rnd(0x00000000, 0xfffffffe); - g0r_state.distortionSeed2 = rnd(0x00000000, 0xfffffffe); - g0r_state.howToDistort1 = rnd (0, inst->colorGlitchIntensity); - g0r_state.howToDistort2 = rnd (0, inst->colorGlitchIntensity); + inst->state.distortionSeed1 = rnd(0x00000000, 0xfffffffe); + inst->state.distortionSeed2 = rnd(0x00000000, 0xfffffffe); + inst->state.howToDistort1 = rnd (0, inst->colorGlitchIntensity); + inst->state.howToDistort2 = rnd (0, inst->colorGlitchIntensity); } } @@ -304,49 +306,49 @@ uint32_t* dst = outframe; const uint32_t* src = inframe; - uint32_t *pixel; - - g0r_state.currentBlock = rnd(1, inst->maxBlockSize); + uint32_t *pixel; + + inst->state.currentBlock = rnd(1, inst->maxBlockSize); for (y = 0; y < inst->height; y++) { - if (g0r_state.currentPos > g0r_state.currentBlock) + if (inst->state.currentPos > inst->state.currentBlock) { glitch0r_state_reset(inst); } else - g0r_state.currentPos++; - - g0r_state.currentY = y*inst->width; - pixel = dst + g0r_state.currentY; - - if (g0r_state.passThisLine) - { - memcpy((uint32_t *)(dst + g0r_state.currentY), - (uint32_t *)(src + g0r_state.currentY), + inst->state.currentPos++; + + inst->state.currentY = y*inst->width; + pixel = dst + inst->state.currentY; + + if (inst->state.passThisLine) + { + memcpy((uint32_t *)(dst + inst->state.currentY), + (uint32_t *)(src + inst->state.currentY), (inst->width) * sizeof(uint32_t)); continue; } - for (x = g0r_state.blkShift; x < (inst->width); x++) - { - *(pixel) = *(src + g0r_state.currentY + x); + for (x = inst->state.blkShift; x < (inst->width); x++) + { + *(pixel) = *(src + inst->state.currentY + x); if (inst->doColorDistortion) glitch0r_pixel_dist0rt(pixel, - g0r_state.distortionSeed1, g0r_state.howToDistort1); + inst->state.distortionSeed1, inst->state.howToDistort1); pixel++; } - for (x = 0; x < g0r_state.blkShift; x++) - { - *(pixel) = *(src + g0r_state.currentY + x); + for (x = 0; x < inst->state.blkShift; x++) + { + *(pixel) = *(src + inst->state.currentY + x); if (inst->doColorDistortion) glitch0r_pixel_dist0rt(pixel, - g0r_state.distortionSeed2, g0r_state.howToDistort2); + inst->state.distortionSeed2, inst->state.howToDistort2); pixel++; } diff --git a/src/filter/heatmap0r/CMakeLists.txt b/src/filter/heatmap0r/CMakeLists.txt index 8e62b3a..dd02722 100644 --- a/src/filter/heatmap0r/CMakeLists.txt +++ b/src/filter/heatmap0r/CMakeLists.txt @@ -6,6 +6,6 @@ endif (MSVC) add_library (${TARGET} MODULE ${SOURCES}) +set_target_properties (${TARGET} PROPERTIES PREFIX "") -# No «lib» prefix (name.so instead of libname.so) -set_target_properties (${TARGET} PROPERTIES PREFIX "") +install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR}) diff --git a/src/filter/heatmap0r/heatmap0r.c b/src/filter/heatmap0r/heatmap0r.c index 6e360cd..faf9b0d 100644 --- a/src/filter/heatmap0r/heatmap0r.c +++ b/src/filter/heatmap0r/heatmap0r.c @@ -96,7 +96,7 @@ info->major_version = 0; info->minor_version = 1; info->num_params = 4; - info->explanation = "Performs a continious trichromatic tinting"; + info->explanation = "Performs a continuous trichromatic tinting"; } void f0r_get_param_info(f0r_param_info_t* info, int param_index) diff --git a/src/filter/posterize/posterize.c b/src/filter/posterize/posterize.c index 931acd1..4842468 100644 --- a/src/filter/posterize/posterize.c +++ b/src/filter/posterize/posterize.c @@ -122,6 +122,9 @@ levelsInput = CLAMP(levelsInput, 0.0, 48.0) + 2.0; int numLevels = (int)levelsInput; + // Prevent division by zero + if (numLevels < 2) numLevels = 2; + // create levels table unsigned char levels[256]; int i; diff --git a/src/filter/rgbnoise/rgbnoise.c b/src/filter/rgbnoise/rgbnoise.c index f51cd90..02fc682 100644 --- a/src/filter/rgbnoise/rgbnoise.c +++ b/src/filter/rgbnoise/rgbnoise.c @@ -3,7 +3,7 @@ * It contains code from plug-ins/common/noise-rgb.c, see that for copyrights. * * rgbnoise.c - * Copyright 2012 Janne Liljeblad + * Copyright 2012 Janne Liljeblad * * This file is a Frei0r plugin. * @@ -29,17 +29,17 @@ #include "frei0r.h" #include "frei0r/math.h" -static int MY_MAX_RAND = 32767;// I assume RAND_MAX to be at least this big. -static double gaussian_lookup[32767]; -static int TABLE_INITED = 0; -static int next_gaussian_index = 0; -static int last_in_range = 32766; +#define MY_MAX_RAND 32767 // assume RAND_MAX to be at least this big. typedef struct rgbnoise_instance { unsigned int width; unsigned int height; double noise; + double gaussian_lookup[MY_MAX_RAND]; + int table_inited; + int next_gaussian_index; + int last_in_range; } rgbnoise_instance_t; @@ -54,9 +54,9 @@ rgbnoiseInfo->plugin_type = F0R_PLUGIN_TYPE_FILTER; rgbnoiseInfo->color_model = F0R_COLOR_MODEL_RGBA8888; rgbnoiseInfo->frei0r_version = FREI0R_MAJOR_VERSION; - rgbnoiseInfo->major_version = 0; - rgbnoiseInfo->minor_version = 9; - rgbnoiseInfo->num_params = 1; + rgbnoiseInfo->major_version = 0; + rgbnoiseInfo->minor_version = 9; + rgbnoiseInfo->num_params = 1; rgbnoiseInfo->explanation = "Adds RGB noise to image."; } @@ -74,9 +74,12 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height) { rgbnoise_instance_t* inst = (rgbnoise_instance_t*)calloc(1, sizeof(*inst)); - inst->width = width; + inst->width = width; inst->height = height; inst->noise = 0.2; + inst->table_inited = 0; + inst->next_gaussian_index = 0; + inst->last_in_range = MY_MAX_RAND; return (f0r_instance_t)inst; } @@ -85,7 +88,7 @@ free(instance); } -void f0r_set_param_value(f0r_instance_t instance, +void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) { rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; @@ -99,9 +102,9 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) -{ +{ rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; - switch (param_index) + switch (param_index) { case 0: *((double*)param) = inst->noise; @@ -133,7 +136,7 @@ return x; } -static void create_new_lookup_range() +static void create_new_lookup_range(rgbnoise_instance_t* inst) { int first, last, tmp; first = rand() % (MY_MAX_RAND - 1); @@ -144,50 +147,54 @@ last = first; first = tmp; } - next_gaussian_index = first; - last_in_range = last; -} - -static inline double next_gauss() -{ - next_gaussian_index++; - if (next_gaussian_index >= last_in_range) - { - create_new_lookup_range(); - } - return gaussian_lookup[next_gaussian_index]; -} - -static inline int addNoise(int sample, double noise) + inst->next_gaussian_index = first; + inst->last_in_range = last; +} + +static inline double next_gauss(rgbnoise_instance_t* inst) +{ + inst->next_gaussian_index++; + if (inst->next_gaussian_index >= inst->last_in_range) + { + create_new_lookup_range(inst); + } + return inst->gaussian_lookup[inst->next_gaussian_index]; +} + +static inline int addNoise(rgbnoise_instance_t* inst, int sample, double noise) { int byteNoise = 0; int noiseSample = 0; - byteNoise = (int) (noise * next_gauss()); + byteNoise = (int) (noise * next_gauss(inst)); noiseSample = sample + byteNoise; noiseSample = CLAMP(noiseSample, 0, 255); return noiseSample; -} +} int f0r_init() { - if (TABLE_INITED == 0) + return 1; +} + +void rgb_noise(f0r_instance_t instance, double time, + const uint32_t* inframe, uint32_t* outframe) +{ + rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; + unsigned int len = inst->width * inst->height; + + // Initialize the gaussian lookup table if not already done + if (inst->table_inited == 0) { int i; for( i = 0; i < MY_MAX_RAND; i++) { - gaussian_lookup[i] = gauss() * 127.0; + inst->gaussian_lookup[i] = gauss() * 127.0; } - TABLE_INITED = 1; - } - return 1; -} - -void rgb_noise(f0r_instance_t instance, double time, - const uint32_t* inframe, uint32_t* outframe) -{ - rgbnoise_instance_t* inst = (rgbnoise_instance_t*)instance; - unsigned int len = inst->width * inst->height; + inst->table_inited = 1; + inst->next_gaussian_index = 0; + inst->last_in_range = MY_MAX_RAND; + } unsigned char* dst = (unsigned char*)outframe; const unsigned char* src = (unsigned char*)inframe; @@ -197,11 +204,11 @@ while (len--) { sample = *src++; - *dst++ = addNoise(sample, noise); + *dst++ = addNoise(inst, sample, noise); sample = *src++; - *dst++ = addNoise(sample, noise); + *dst++ = addNoise(inst, sample, noise); sample = *src++; - *dst++ = addNoise(sample, noise); + *dst++ = addNoise(inst, sample, noise); *dst++ = *src++; } } @@ -213,4 +220,3 @@ assert(instance); rgb_noise(instance, time, inframe, outframe); } - diff --git a/src/filter/rgbsplit0r/rgbsplit0r.c b/src/filter/rgbsplit0r/rgbsplit0r.c index 3a3014a..0c57eae 100644 --- a/src/filter/rgbsplit0r/rgbsplit0r.c +++ b/src/filter/rgbsplit0r/rgbsplit0r.c @@ -1,7 +1,7 @@ /* rgbsplit0r.c * Copyright (C) 2016 IDENT Software ~ http://identsoft.org * Inspired by the witch house and web culture - * + * * This file is a Frei0r plugin. * * This program is free software; you can redistribute it and/or modify @@ -111,7 +111,6 @@ inst->width = width; inst->height = height; inst->shiftY = 0; inst->shiftX = 0; - return (f0r_instance_t)inst; } @@ -120,7 +119,7 @@ free(instance); } -void f0r_set_param_value(f0r_instance_t instance, +void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_index) { assert(instance); @@ -135,7 +134,10 @@ double shiftY = *((double*)param) - 0.5; // Convert to range from 0 to one eighth of height - shiftY = ((inst->height / 8) * shiftY); + if (inst->height > 0) + shiftY = ((inst->height / 8) * shiftY); + else + shiftY = 0; inst->shiftY = (unsigned int)shiftY; break; @@ -145,9 +147,12 @@ { // scale to [-1/16..1/16] double shiftX = *((double*)param) - 0.5; - + // Convert to range from 0 to one eighth of width - shiftX = ((inst->width / 8) * shiftX); + if (inst->width > 0) + shiftX = ((inst->width / 8) * shiftX); + else + shiftX = 0; inst->shiftX = (unsigned int)shiftX; break; @@ -167,14 +172,20 @@ case 0 : // vertical shift { // convert plugin's param to frei0r range - *((double*)param) = (inst->shiftY) / (inst->height / 8) + 0.5; + if (inst->height > 0) + *((double*)param) = (inst->shiftY) / (inst->height / 8) + 0.5; + else + *((double*)param) = 0.5; break; } case 1 : // horizontal shift { // convert plugin's param to frei0r range - *((double*)param) = (inst->shiftX) / (inst->width / 8) + 0.5; + if (inst->width > 0) + *((double*)param) = (inst->shiftX) / (inst->width / 8) + 0.5; + else + *((double*)param) = 0.5; break; } } @@ -195,8 +206,8 @@ uint32_t pxR = 0, pxG = 0, pxB = 0; // First make a blue layer shifted back - if (((x - inst->shiftX) < inst->width) && - ((y - inst->shiftY) < inst->height)) + if (((int)x >= (int)inst->shiftX) && + ((int)y >= (int)inst->shiftY)) { rgbsplit0r_extract_color((uint32_t *)(src + (x - inst->shiftX) + @@ -221,4 +232,3 @@ *(dst + x + (y*inst->width)) = (pxG | pxB | pxR); } } - diff --git a/src/filter/sobel/sobel.cpp b/src/filter/sobel/sobel.cpp index f05860f..447ff97 100644 --- a/src/filter/sobel/sobel.cpp +++ b/src/filter/sobel/sobel.cpp @@ -26,12 +26,16 @@ public: sobel(unsigned int width, unsigned int height) { + this->width = width; + this->height = height; } virtual void update(double time, uint32_t* out, const uint32_t* in) { + if (width == 0 || height == 0) return; + std::copy(in, in + width*height, out); for (unsigned int y=1; y #include -#ifdef __SSE4_1__ +/* Check for SSE4.1 support */ +#if defined(__SSE4_1__) #include +#define USE_SSE4_1 1 +#else +#define USE_SSE4_1 0 +#endif + +/* Check for other SIMD instruction sets */ +#if defined(__AVX__) && defined(__AVX2__) +#include +#define USE_AVX2 1 +#else +#define USE_AVX2 0 +#endif + +#if defined(__ARM_NEON__) || defined(__ARM_NEON) +#include +#define USE_NEON 1 +#else +#define USE_NEON 0 #endif #include @@ -46,17 +65,17 @@ void f0r_deinit() { /* no initialization required */ } -void f0r_get_plugin_info(f0r_plugin_info_t* tint0r_instance_t) -{ - tint0r_instance_t->name = "Tint0r"; - tint0r_instance_t->author = "Maksim Golovkin & Cynthia"; - tint0r_instance_t->plugin_type = F0R_PLUGIN_TYPE_FILTER; - tint0r_instance_t->color_model = F0R_COLOR_MODEL_BGRA8888; - tint0r_instance_t->frei0r_version = FREI0R_MAJOR_VERSION; - tint0r_instance_t->major_version = 0; - tint0r_instance_t->minor_version = 1; - tint0r_instance_t->num_params = 3; - tint0r_instance_t->explanation = "Tint a source image with specified colors"; +void f0r_get_plugin_info(f0r_plugin_info_t* info) +{ + info->name = "Tint0r"; + info->author = "Maksim Golovkin & Cynthia"; + info->plugin_type = F0R_PLUGIN_TYPE_FILTER; + info->color_model = F0R_COLOR_MODEL_BGRA8888; + info->frei0r_version = FREI0R_MAJOR_VERSION; + info->major_version = 0; + info->minor_version = 1; + info->num_params = 3; + info->explanation = "Tint a source image with specified colors"; } void f0r_get_param_info(f0r_param_info_t* info, int param_index) @@ -143,70 +162,44 @@ } } -#ifndef __SSE4_1__ static inline unsigned char map_color(double amount, double comp_amount, float color, float luma, float minColor, float maxColor) { double val = (comp_amount * color) + amount * (luma * (maxColor - minColor) + minColor); return (unsigned char)(255*CLAMP(val, 0, 1)); } -#endif - -void f0r_update(f0r_instance_t instance, double time, - const uint32_t* inframe, uint32_t* outframe) -{ - assert(instance); - tint0r_instance_t* inst = (tint0r_instance_t*)instance; - - #ifdef __SSE4_1__ - size_t len = (inst->width * inst->height) / 4; - - const __m128 weights = _mm_set_ps(0.0, 0.299, 0.587, 0.114), - amount = _mm_set1_ps(inst->amount), + +#if USE_SSE4_1 +static void tint_sse41(const uint32_t* inframe, uint32_t* outframe, size_t len, + double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) +{ + const __m128 weights = _mm_set_ps(0.0, 0.114, 0.587, 0.299), + sse_amount = _mm_set1_ps(amount), /* Pass the alpha channel */ comp_amount = _mm_set_ps(1.0, - 1.0 - inst->amount, - 1.0 - inst->amount, - 1.0 - inst->amount); - - f0r_param_color_t black = inst->blackColor, - white = inst->whiteColor; + 1.0 - amount, + 1.0 - amount, + 1.0 - amount); /* Zero the alpha component to exclude it from calculations. */ - const __m128 cmin = _mm_set_ps(0.0, black.r, black.g, black.b), - cdelta = _mm_sub_ps(_mm_set_ps(0.0, white.r, white.g, white.b), cmin), - tmp0 = _mm_mul_ps(cdelta, amount), - tmp1 = _mm_mul_ps(_mm_mul_ps(amount, _mm_set1_ps(255.0)), cmin); + const __m128 cmin = _mm_set_ps(0.0, blackColor.b, blackColor.g, blackColor.r), + cdelta = _mm_sub_ps(_mm_set_ps(0.0, whiteColor.b, whiteColor.g, whiteColor.r), cmin), + tmp0 = _mm_mul_ps(cdelta, sse_amount), + tmp1 = _mm_mul_ps(_mm_mul_ps(sse_amount, _mm_set1_ps(255.0)), cmin); __m128 p, p0, p1, p2, p3, luma; - #else - unsigned int len = inst->width * inst->height; - double amount = inst->amount; - double comp_amount = 1.0 - inst->amount; - - unsigned char* dst = (unsigned char*)outframe; - const unsigned char* src = (unsigned char*)inframe; - float b, g, r; - float luma; - #endif - - while (len--) - { - #ifdef __SSE4_1__ + + // Process pixels in groups of 4 + for (size_t i = 0; i < len; i++) + { /* Load four pixels at once. */ - p = _mm_loadu_si128((__m128i*)inframe); + p = _mm_loadu_si128((__m128i*)(inframe + i * 4)); /* Extract four pixels into separate XMM registers and convert them to float. */ p0 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(p)); p1 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 4))); p2 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 8))); p3 = _mm_cvtepi32_ps(_mm_cvtepu8_epi32(_mm_srli_si128(p, 12))); - #else - b = *src++ / 255.; - g = *src++ / 255.; - r = *src++ / 255.; - #endif - - #ifdef __SSE4_1__ + #define tint(v) \ luma = _mm_dp_ps((v), weights, 0x7F); \ v = _mm_add_ps(_mm_mul_ps(comp_amount, (v)), \ @@ -219,19 +212,81 @@ p = _mm_packus_epi16(_mm_packus_epi32(p0, p1), _mm_packus_epi32(p2, p3)); - _mm_storeu_si128((__m128i*)outframe, p); - - /* Stride of 128 bits; i.e. 16 bytes */ - inframe += 4; - outframe += 4; - #else - luma = (b * .114 + g * .587 + r * .299); - - *dst++ = map_color(amount, comp_amount, b, luma, inst->blackColor.b, inst->whiteColor.b); - *dst++ = map_color(amount, comp_amount, g, luma, inst->blackColor.g, inst->whiteColor.g); - *dst++ = map_color(amount, comp_amount, r, luma, inst->blackColor.r, inst->whiteColor.r); - *dst++ = *src++; - #endif - } -} - + _mm_storeu_si128((__m128i*)(outframe + i * 4), p); + } +} +#elif USE_AVX2 +static void tint_avx2(const uint32_t* inframe, uint32_t* outframe, size_t len, + double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) +{ + // AVX2 implementation would go here + // For now, fall back to scalar implementation + // This is a placeholder for a future AVX2 implementation +} +#elif USE_NEON +static void tint_neon(const uint32_t* inframe, uint32_t* outframe, size_t len, + double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) +{ + // NEON implementation would go here + // For now, fall back to scalar implementation + // This is a placeholder for a future NEON implementation +} +#endif + +static void tint_scalar(const uint32_t* inframe, uint32_t* outframe, size_t len, + double amount, f0r_param_color_t blackColor, f0r_param_color_t whiteColor) +{ + double comp_amount = 1.0 - amount; + + const unsigned char* src = (const unsigned char*)inframe; + unsigned char* dst = (unsigned char*)outframe; + + float b, g, r; + float luma; + + while (len--) + { + b = src[0] / 255.0f; + g = src[1] / 255.0f; + r = src[2] / 255.0f; + + luma = (b * 0.114f + g * 0.587f + r * 0.299f); + + dst[0] = map_color(amount, comp_amount, b, luma, blackColor.b, whiteColor.b); + dst[1] = map_color(amount, comp_amount, g, luma, blackColor.g, whiteColor.g); + dst[2] = map_color(amount, comp_amount, r, luma, blackColor.r, whiteColor.r); + dst[3] = src[3]; // Copy alpha + + src += 4; + dst += 4; + } +} + +void f0r_update(f0r_instance_t instance, double time, + const uint32_t* inframe, uint32_t* outframe) +{ + assert(instance); + tint0r_instance_t* inst = (tint0r_instance_t*)instance; + size_t len = inst->width * inst->height; + +#if USE_SSE4_1 + // Process in chunks of 4 pixels for SSE + size_t sse_len = len / 4; + size_t remainder = len % 4; + + if (sse_len > 0) { + tint_sse41(inframe, outframe, sse_len, inst->amount, inst->blackColor, inst->whiteColor); + } + + // Handle remaining pixels with scalar implementation + if (remainder > 0) { + const uint32_t* remaining_in = inframe + (sse_len * 4); + uint32_t* remaining_out = outframe + (sse_len * 4); + tint_scalar(remaining_in, remaining_out, remainder, inst->amount, inst->blackColor, inst->whiteColor); + } +#else + // Use scalar implementation for all pixels + tint_scalar(inframe, outframe, len, inst->amount, inst->blackColor, inst->whiteColor); +#endif +} + diff --git a/src/filter/water/water.cpp b/src/filter/water/water.cpp index 66073d5..be26453 100644 --- a/src/filter/water/water.cpp +++ b/src/filter/water/water.cpp @@ -1,7 +1,7 @@ /* Water filter * - * (c) Copyright 2000-2007 Denis Rojo - * + * (c) Copyright 2000-2025 Denis Roio + * * from an original idea of water algorithm by Federico 'Pix' Feroldi * * this code contains optimizations by Jason Hood and Scott Scriven @@ -10,7 +10,7 @@ * ported to C++ and frei0r plugin API in 2007 * * This source code is free software; you can redistribute it and/or - * modify it under the terms of the GNU Public License as published + * modify it under the terms of the GNU Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * @@ -22,8 +22,6 @@ * You should have received a copy of the GNU Public License along with * this source code; if not, write to: * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * "$Id: water.c 193 2004-06-01 11:00:25Z jaromil $" * */ @@ -81,8 +79,9 @@ surfer = 0; distort = 0; smooth = 0; - position.x = 0; - position.y = 0; + position.x = 0.0; + position.y = 0.0; + //randomize_swirl = false; register_param(physics, "physics", "water density: from 0.0 to 1.0"); register_param(swirl, "swirl", "swirling whirpool in the center"); register_param(rain, "rain", "rain drops all over"); @@ -120,11 +119,11 @@ water_surfacesize = geo->size; calc_optimization = (height)*(width); - + xang = fastrand()%2048; yang = fastrand()%2048; swirlangle = fastrand()%2048; - + /* buffer allocation tango */ if ( width*height > 0 ) { Height[0] = (uint32_t*)calloc(width*(height+1), sizeof(uint32_t)); @@ -135,6 +134,9 @@ BkGdImage = (uint32_t*) malloc(geo->size); BkGdImagePost = (uint32_t*)malloc(geo->size); } + + // Initialize surface to NULL + surface = NULL; } ~Water() { @@ -152,7 +154,7 @@ memcpy(BkGdImage, in, width*height*sizeof(uint32_t)); water_update(out); } - + private: ScreenGeometry *geo; @@ -162,11 +164,11 @@ uint32_t *BkGdImagePre; uint32_t *BkGdImage; uint32_t *BkGdImagePost; - + // uint32_t *buffer; - + void *surface; - + /* water effect variables */ int Hpage; int xang, yang; @@ -174,39 +176,39 @@ int x, y, ox, oy; int done; int mode; - + /* precalculated to optimize a bit */ int water_surfacesize; int calc_optimization; - + /* density: water density (step 1) pheight: splash height (step 40) radius: waterdrop radius (step 1) */ int density, pheight, radius; - int offset; - + int offset; + int raincount; int blend; void water_clear(); void water_distort(); void water_setphysics(double physics); - void water_update(uint32_t* out); + void water_update(uint32_t *out); void water_drop(int x, int y); void water_bigsplash(int x, int y); void water_surfer(); void water_swirl(); void water_3swirls(); - - void DrawWater(int page,uint32_t* out); + + void DrawWater(int page, uint32_t* out); void CalcWater(int npage, int density); void CalcWaterBigFilter(int npage, int density); - + void SmoothWater(int npage); - + void HeightBlob(int x, int y, int radius, int height, int page); void HeightBox (int x, int y, int radius, int height, int page); - + void WarpBlob(int x, int y, int radius, int height, int page); void SineBlob(int x, int y, int radius, int height, int page); @@ -215,7 +217,7 @@ int FSin(int angle) { return FSinTab[angle&FSINMAX]; } int FCos(int angle) { return FCosTab[angle&FSINMAX]; } void FCreateSines() { - int i; double angle; + int i; double angle; for(i=0; i<2048; i++) { angle = (float)i * (PI/1024.0); FSinTab[i] = (int)(sin(angle) * (float)0x10000); @@ -227,7 +229,7 @@ uint32_t randval; uint32_t fastrand() { return (randval=randval*1103515245+12345); }; void fastsrand(uint32_t seed) { randval = seed; }; - + /* integer optimized square root by jaromil */ int isqrt(unsigned int x) { unsigned int m, y, b; m = 0x40000000; @@ -235,7 +237,7 @@ if(x>=b) { x=x-b; y=y|m; } m=m>>2; } return y; } - + }; void Water::water_clear() { @@ -306,6 +308,7 @@ } void Water::water_surfer() { + int x, y; x = (geo->w>>1) + (( ( @@ -322,7 +325,7 @@ ) >> 16); xang += 13; yang += 12; - + if(mode & 0x4000) { offset = (oy+y)/2*geo->w + ((ox+x)>>1); // QUAAA @@ -331,7 +334,7 @@ Height[Hpage][offset - 1] = Height[Hpage][offset + geo->w] = Height[Hpage][offset - geo->w] = pheight >> 1; - + offset = y*geo->w + x; Height[Hpage][offset] = pheight<<1; Height[Hpage][offset + 1] = @@ -344,23 +347,24 @@ SineBlob(((ox+x)>>1), ((oy+y)>>1), 3, -1200, Hpage); SineBlob(x, y, 4, -2000, Hpage); } - + ox = x; - oy = y; + oy = y; } void Water::water_swirl() { + int x, y; x = (geo->w>>1) + (( (FCos(swirlangle)) * (25) ) >> 16); - + y = (geo->h>>1) + (( (FSin(swirlangle)) * (25) ) >> 16); - x += position.x; - y += position.y; + x += (int)(position.x * geo->w); + y += (int)(position.y * geo->h); swirlangle += 50; if(mode & 0x4000) @@ -371,6 +375,7 @@ void Water::water_3swirls() { #define ANGLE 15 + int x, y; x = (95) + (( (FCos(swirlangle)) * (ANGLE) @@ -382,7 +387,7 @@ if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); else WarpBlob(x, y, radius, pheight, Hpage); - + x = (95) + (( (FCos(swirlangle)) * (ANGLE) @@ -391,10 +396,10 @@ + (( (FSin(swirlangle)) * (ANGLE) ) >> 16); - + if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); else WarpBlob(x, y, radius, pheight, Hpage); - + x = (345) + (( (FCos(swirlangle)) * (ANGLE) @@ -403,7 +408,7 @@ + (( (FSin(swirlangle)) * (ANGLE) ) >> 16); - + if(mode & 0x4000) HeightBlob(x,y, radius>>2, pheight, Hpage); else WarpBlob(x, y, radius, pheight, Hpage); @@ -413,27 +418,27 @@ /* internal physics routines */ void Water::DrawWater(int page,uint32_t* out) { int dx, dy; - int x, y; - uint32_t offset=geo->w + 1; - uint32_t newoffset; + int offset=geo->w + 1; + int newoffset; + int maxoffset = geo->w * geo->h; int *ptr = (int*)&Height[page][0]; - int maxoffset=geo->size/sizeof(uint32_t); - - for (y = calc_optimization; offset < y; offset+=2) { - for (x = offset+geo->w-2; offset < x; offset++) { - + + for (int y = calc_optimization; offset < y; offset += 2) { + for (int x = offset+geo->w-2; offset < x; offset++) { dx = ptr[offset] - ptr[offset+1]; dy = ptr[offset] - ptr[offset+geo->w]; newoffset = offset + geo->w*(dy>>3) + (dx>>3); - if(newoffsetw]; newoffset = offset + geo->w*(dy>>3) + (dx>>3); - if(newoffsetw + 1; int *newptr = (int*) &Height[npage][0]; int *oldptr = (int*) &Height[npage^1][0]; - int x, y; - - for (y = calc_optimization; count < y; count += 2) { - for (x = count+geo->w-2; count < x; count++) { + + for (int y = calc_optimization; count < y; count += 2) { + for (int x = count+geo->w-2; count < x; count++) { /* eight pixels */ newh = ((oldptr[count + geo->w] + oldptr[count - geo->w] @@ -468,10 +472,9 @@ int count = geo->w + 1; int *newptr = (int*) &Height[npage][0]; int *oldptr = (int*) &Height[npage^1][0]; - int x, y; - - for(y=1; yh-1; y++) { - for(x=1; xw-1; x++) { + + for(int y=1; yh-1; y++) { + for(int x=1; xw-1; x++) { /* eight pixel */ newh = ((oldptr[count + geo->w] + oldptr[count - geo->w] @@ -483,8 +486,8 @@ + oldptr[count + geo->w + 1] ) >> 3 ) + newptr[count]; - - + + newptr[count] = newh>>1; count++; } @@ -497,10 +500,9 @@ int count = (geo->w<<1) + 2; int *newptr = (int*) &Height[npage][0]; int *oldptr = (int*) &Height[npage^1][0]; - int x, y; - - for(y=2; yh-2; y++) { - for(x=2; xw-2; x++) { + + for(int y=2; yh-2; y++) { + for(int x=2; xw-2; x++) { /* 25 pixels */ newh = ( ( @@ -559,8 +561,13 @@ for(cy = top; cy < bottom; cy++) { cyq = cy*cy; for(cx = left; cx < right; cx++) { - if(cx*cx + cyq < rquad) - Height[page][geo->w*(cy+y) + (cx+x)] += height; + if(cx*cx + cyq < rquad) { + int index = geo->w*(cy+y) + (cx+x); + // Bounds check + if (index >= 0 && index < geo->w * geo->h) { + Height[page][index] += height; + } + } } } } @@ -572,17 +579,21 @@ if(x<0) x = 1+radius+ fastrand()%(geo->w-2*radius-1); if(y<0) y = 1+radius+ fastrand()%(geo->h-2*radius-1); - + left=-radius; right = radius; top=-radius; bottom = radius; - + CLIP_EDGES - + for(cy = top; cy < bottom; cy++) { for(cx = left; cx < right; cx++) { - Height[page][geo->w*(cy+y) + (cx+x)] = height; - } - } + int index = geo->w*(cy+y) + (cx+x); + // Bounds check + if (index >= 0 && index < geo->w * geo->h) { + Height[page][index] = height; + } + } + } } void Water::WarpBlob(int x, int y, int radius, int height, int page) { @@ -590,22 +601,25 @@ int left,top,right,bottom; int square; int radsquare = radius * radius; - + radsquare = (radius*radius); - + height = height>>5; - + left=-radius; right = radius; top=-radius; bottom = radius; CLIP_EDGES - + for(cy = top; cy < bottom; cy++) { for(cx = left; cx < right; cx++) { square = cy*cy + cx*cx; if(square < radsquare) { - Height[page][geo->w*(cy+y) + cx+x] - += (int)((radius-isqrt(square))*(float)(height)); + int index = geo->w*(cy+y) + cx+x; + // Bounds check + if (index >= 0 && index < geo->w * geo->h) { + Height[page][index] += (int)((radius-isqrt(square))*(float)(height)); + } } } } @@ -617,7 +631,7 @@ int square, dist; int radsquare = radius * radius; float length = (1024.0/(float)radius)*(1024.0/(float)radius); - + if(x<0) x = 1+radius+ fastrand()%(geo->w-2*radius-1); if(y<0) y = 1+radius+ fastrand()%(geo->h-2*radius-1); @@ -632,8 +646,11 @@ square = cy*cy + cx*cx; if(square < radsquare) { dist = (int)(isqrt(square*length)); - Height[page][geo->w*(cy+y) + cx+x] - += (int)((FCos(dist)+0xffff)*(height)) >> 19; + int index = geo->w*(cy+y) + cx+x; + // Bounds check + if (index >= 0 && index < geo->w * geo->h) { + Height[page][index] += (int)((FCos(dist)+0xffff)*(height)) >> 19; + } } } } diff --git a/src/mixer2/blend/blend.cpp b/src/mixer2/blend/blend.cpp index 20ac884..a6108e5 100644 --- a/src/mixer2/blend/blend.cpp +++ b/src/mixer2/blend/blend.cpp @@ -29,6 +29,9 @@ public: blend(unsigned int width, unsigned int height) { + this->width = width; + this->height = height; + this->size = width * height; blend_factor = 0.5; register_param(blend_factor,"blend","blend factor"); } @@ -45,6 +48,11 @@ const uint32_t* in1, const uint32_t* in2) { + // Validate inputs + if (!out || !in1 || !in2) { + return; + } + const uint8_t *src1 = reinterpret_cast(in1); const uint8_t *src2 = reinterpret_cast(in2); uint8_t *dst = reinterpret_cast(out); @@ -52,12 +60,17 @@ const uint8_t one_minus_bf = (255 - bf); uint32_t w = size; uint32_t b; - + + // Validate size + if (w == 0) { + return; + } + while (w--) { for (b = 0; b < NBYTES; b++) dst[b] = (src1[b] * one_minus_bf + src2[b] * bf) / 255; - + src1 += NBYTES; src2 += NBYTES; dst += NBYTES; diff --git a/src/mixer2/cairoblend/cairoblend.c b/src/mixer2/cairoblend/cairoblend.c index 1fa1ac0..0d1feca 100644 --- a/src/mixer2/cairoblend/cairoblend.c +++ b/src/mixer2/cairoblend/cairoblend.c @@ -77,23 +77,42 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height) { + // Validate inputs + if (width == 0 || height == 0) { + return NULL; + } + cairo_blend_instance_t* inst = (cairo_blend_instance_t*)calloc(1, sizeof(*inst)); - inst->width = width; + if (!inst) { + return NULL; + } + + inst->width = width; inst->height = height; inst->opacity = 1.0; - const char* blend_val = NORMAL; - inst->blend_mode = (char*) malloc (strlen(blend_val) + 1 ); - strcpy (inst->blend_mode, blend_val); + const char* blend_val = NORMAL; + inst->blend_mode = (char*) malloc (strlen(blend_val) + 1); + if (!inst->blend_mode) { + free(inst); + return NULL; + } + strcpy (inst->blend_mode, blend_val); return (f0r_instance_t)inst; } void f0r_destruct(f0r_instance_t instance) { + if (!instance) { + return; + } + cairo_blend_instance_t* inst = (cairo_blend_instance_t*)instance; - free(inst->blend_mode); + if (inst->blend_mode) { + free(inst->blend_mode); + } free(instance); } @@ -104,12 +123,22 @@ char* sval; switch(param_index) { case 0: - inst->opacity = *((double*)param); + // Validate double parameter + if (param) { + inst->opacity = *((double*)param); + } break; case 1: - sval = (*(char**)param); - inst->blend_mode = (char*)realloc (inst->blend_mode, strlen(sval) + 1); - strcpy (inst->blend_mode, sval); + // Validate string parameter + if (param) { + sval = (*(char**)param); + if (sval) { + inst->blend_mode = (char*)realloc (inst->blend_mode, strlen(sval) + 1); + if (inst->blend_mode) { + strcpy (inst->blend_mode, sval); + } + } + } break; } } @@ -121,10 +150,16 @@ switch(param_index) { case 0: - *((double*)param) = inst->opacity; + if (param) { + *((double*)param) = inst->opacity; + } break; case 1: - *((f0r_param_string *)param) = inst->blend_mode; + if (param && inst->blend_mode) { + *((f0r_param_string *)param) = inst->blend_mode; + } else if (param) { + *((f0r_param_string *)param) = ""; + } break; } } @@ -134,28 +169,54 @@ int w = inst->width; int h = inst->height; int stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, w); + + // Validate inputs + if (!inst || !out || !src || w <= 0 || h <= 0) { + return; + } cairo_surface_t* out_image = cairo_image_surface_create_for_data (out, CAIRO_FORMAT_ARGB32, w, h, stride); + // Check if surface creation succeeded + if (!out_image) { + return; + } + cairo_t* cr = cairo_create (out_image); + // Check if context creation succeeded + if (!cr) { + cairo_surface_destroy (out_image); + return; + } cairo_surface_t* src_image = cairo_image_surface_create_for_data ((unsigned char*)src, CAIRO_FORMAT_ARGB32, w, h, stride); - - // Set source, blen mode and draw with current opacity - frei0r_cairo_set_operator(cr, inst->blend_mode); + // Check if surface creation succeeded + if (!src_image) { + cairo_destroy (cr); + cairo_surface_destroy (out_image); + return; + } + + // Validate blend mode string + if (inst->blend_mode) { + // Set source, blend mode and draw with current opacity + frei0r_cairo_set_operator(cr, inst->blend_mode); + } + cairo_set_source_surface (cr, src_image, 0, 0); cairo_paint_with_alpha (cr, inst->opacity); - cairo_surface_destroy (out_image); + // Clean up in proper order cairo_surface_destroy (src_image); cairo_destroy (cr); + cairo_surface_destroy (out_image); } void f0r_update(f0r_instance_t instance, double time, @@ -167,6 +228,11 @@ void f0r_update2(f0r_instance_t instance, double time, const uint32_t* inframe1, const uint32_t* inframe2, const uint32_t* inframe3, uint32_t* outframe) { + // Validate inputs + if (!instance || !inframe1 || !inframe2 || !outframe) { + return; + } + assert(instance); cairo_blend_instance_t* inst = (cairo_blend_instance_t*) instance; @@ -175,6 +241,11 @@ unsigned char* out = (unsigned char*)outframe; int pixels = inst->width * inst->height; + // Validate dimensions + if (pixels <= 0) { + return; + } + frei0r_cairo_premultiply_rgba2 (dst, out, pixels, -1); frei0r_cairo_premultiply_rgba (src, pixels, -1); draw_composite (inst, out, src, time); diff --git a/src/mixer2/overlay/overlay.cpp b/src/mixer2/overlay/overlay.cpp index dd48fb7..23c9498 100644 --- a/src/mixer2/overlay/overlay.cpp +++ b/src/mixer2/overlay/overlay.cpp @@ -29,6 +29,9 @@ public: overlay(unsigned int width, unsigned int height) { + this->width = width; + this->height = height; + this->size = width * height; } /** @@ -44,22 +47,32 @@ const uint32_t* in1, const uint32_t* in2) { + // Validate inputs + if (!out || !in1 || !in2) { + return; + } + const uint8_t *src1 = reinterpret_cast(in1); const uint8_t *src2 = reinterpret_cast(in2); uint8_t *dst = reinterpret_cast(out); uint32_t sizeCounter = size; - + + // Validate size + if (sizeCounter == 0) { + return; + } + uint32_t b, tmp, tmpM; - + while (sizeCounter--) { for (b = 0; b < ALPHA; b++) { dst[b] = INT_MULT(src1[b], src1[b] + INT_MULT(2 * src2[b], 255 - src1[b], tmpM), tmp); } - + dst[ALPHA] = MIN(src1[ALPHA], src2[ALPHA]); - + src1 += NBYTES; src2 += NBYTES; dst += NBYTES;