Compare commits

...

9 Commits

Author SHA1 Message Date
Jaromil
f081aa64d4 Minor fixes following up review
use CLAMP in colordistance

keep MY_MAX_RAND in rgbnoise

code cleanup from old experiments

leaner free section in water

Co-authored-by: Dan Dennedy <dan@dennedy.org>
2025-12-02 17:23:47 +01:00
Jaromil
542c4bc0a9 fix: g0r_state fix leftover 2025-12-02 17:23:47 +01:00
Jaromil
ce386ca806 fix: safety and readability of water filter
long due update, not yet fully tested
2025-12-02 17:23:47 +01:00
Jaromil
7a044cba04 fix: WITHOUT_FACERECOGNITION build flag
to avoid double loading of protobuf, implicitly loaded by opencv,
we need to deactivate the build of two face detection plugins that
use opencv (which loads protobuf implicitly). This fixes bug #185
2025-12-02 17:23:47 +01:00
Jaromil
27af2abfb4 fix: add more checks to mixers and cairo dep
A bunch of safety checks on mixers like overlay, blend and cairoblend
that may mitigate the #187 undeterministic behavior and segfaults.
Could not reproduce it yet.
2025-12-02 17:23:47 +01:00
Jaromil
3a720e4270 fix: thread safety issues
- RGBNoise: move global vars (gaussian_lookup, TABLE_INITED, next_gaussian_index, last_in_range)
- Glitch0r: move global g0r_state
- Cartoon: fix PIXELAT macro to refer to instance

address #188 (seems not all filters on that list are unsafe)
2025-12-02 17:23:47 +01:00
Jaromil
b0a990ed18 fix: cmake lower version policy
With CMake 4, the lowest supported version is 3.12. Anything older will fail to configure.

fix #228
2025-12-02 17:23:47 +01:00
Jaromil
33aaa39ac7 fix: tint0r portability with scalar implementation
- Added portable fallback implementation for tint0r filter when SSE4.1 is not available
- Implemented proper architecture detection using #define directives
- Added support for future AVX2 and NEON optimizations (placeholders)
- Fixed parameter name issue in f0r_get_plugin_info function
- Proper handling of pixel data in BGRA format
- Correct luminance calculation for all code paths
2025-12-02 17:23:47 +01:00
Jaromil
69d0711057 fix: various safety fixes easy to apply
addressed some defective filters known to cause problems

- Fixed division by zero in IIRblur filter
- Added bounds checking in distort0r filter
- Fixed negative index issues in rgbsplit0r filter
- Added division by zero protection in rgbsplit0r filter
- Improved colordistance filter by removing debug printf and adding clamping
- Fixed width/height initialization in sobel filter
- Add bounds checking to sobel filter
- Fixed potential division by zero in posterize filter
2025-12-02 17:23:47 +01:00
19 changed files with 538 additions and 283 deletions

View File

@@ -10,6 +10,10 @@ The presence of optional libraries on the system will trigger compilation of ext
+ [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.
```
@@ -18,6 +22,13 @@ cd build && cmake ../
make
```
To disable face recognition plugins (recommended when using with MLT):
```
mkdir -p build
cd build && cmake -DWITHOUT_FACERECOGNITION=ON ../
make
```
Also ninja and nmake are supported through cmake:
```
cmake -G 'Ninja' ../

View File

@@ -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 ()

View File

@@ -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 @@ double frei0r_cairo_get_scale (double norm_scale)
*/
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_premultiply_rgba (unsigned char *rgba, int pixels, int alpha)
*/
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_unpremultiply_rgba (unsigned char *rgba, int pixels)
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];

View File

@@ -80,12 +80,13 @@ float AitNev3(int t, float xt[], float yt[], float x)
{
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 ((x<xt[0])||(x>xt[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<xt[0]) return yt[0];
else return yt[t-1];
}
//poisce, katere tocke bo uporabil
@@ -98,7 +99,13 @@ float AitNev3(int t, float xt[], float yt[], float x)
for (j=1;j<4;j++)
for (i=(4-1);i>=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];
}

View File

@@ -46,7 +46,7 @@ typedef struct {
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 Cartoon::GetMaxContrast(int32_t *src,int x,int y) {
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);
}

View File

@@ -125,16 +125,17 @@ void f0r_update(f0r_instance_t instance, double time,
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);

View File

@@ -254,16 +254,21 @@ void interpolateGrid(grid_point_t* grid, unsigned int w, unsigned int h,
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;

View File

@@ -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()

View File

@@ -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()

View File

@@ -45,7 +45,7 @@ struct glitch0r_state // helps to save time when allocating in a loop
short int howToDistort1;
short int howToDistort2;
short int passThisLine;
} g0r_state;
};
typedef struct glitch0r_instance
{
@@ -58,6 +58,8 @@ typedef struct glitch0r_instance
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 unsigned int rnd (unsigned int min, unsigned int max)
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 @@ void f0r_update(f0r_instance_t instance, double time,
uint32_t* dst = outframe;
const uint32_t* src = inframe;
uint32_t *pixel;
uint32_t *pixel;
g0r_state.currentBlock = rnd(1, inst->maxBlockSize);
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++;
inst->state.currentPos++;
g0r_state.currentY = y*inst->width;
pixel = dst + g0r_state.currentY;
inst->state.currentY = y*inst->width;
pixel = dst + inst->state.currentY;
if (g0r_state.passThisLine)
if (inst->state.passThisLine)
{
memcpy((uint32_t *)(dst + g0r_state.currentY),
(uint32_t *)(src + g0r_state.currentY),
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++)
for (x = inst->state.blkShift; x < (inst->width); x++)
{
*(pixel) = *(src + g0r_state.currentY + 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++)
for (x = 0; x < inst->state.blkShift; x++)
{
*(pixel) = *(src + g0r_state.currentY + 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++;
}

View File

@@ -122,6 +122,9 @@ void f0r_update(f0r_instance_t instance, double time,
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;

View File

@@ -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 @@ void f0r_get_plugin_info(f0r_plugin_info_t* rgbnoiseInfo)
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 @@ void f0r_get_param_info(f0r_param_info_t* info, int param_index)
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 @@ void f0r_destruct(f0r_instance_t instance)
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_set_param_value(f0r_instance_t instance,
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 @@ static inline double gauss()
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,42 +147,33 @@ static void create_new_lookup_range()
last = first;
first = tmp;
}
next_gaussian_index = first;
last_in_range = last;
inst->next_gaussian_index = first;
inst->last_in_range = last;
}
static inline double next_gauss()
static inline double next_gauss(rgbnoise_instance_t* inst)
{
next_gaussian_index++;
if (next_gaussian_index >= last_in_range)
inst->next_gaussian_index++;
if (inst->next_gaussian_index >= inst->last_in_range)
{
create_new_lookup_range();
create_new_lookup_range(inst);
}
return gaussian_lookup[next_gaussian_index];
return inst->gaussian_lookup[inst->next_gaussian_index];
}
static inline int addNoise(int sample, double noise)
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)
{
int i;
for( i = 0; i < MY_MAX_RAND; i++)
{
gaussian_lookup[i] = gauss() * 127.0;
}
TABLE_INITED = 1;
}
return 1;
}
@@ -189,6 +183,19 @@ void rgb_noise(f0r_instance_t instance, double time,
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++)
{
inst->gaussian_lookup[i] = gauss() * 127.0;
}
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 @@ void rgb_noise(f0r_instance_t instance, double time,
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 @@ void f0r_update(f0r_instance_t instance, double time,
assert(instance);
rgb_noise(instance, time, inframe, outframe);
}

View File

@@ -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 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
inst->width = width; inst->height = height;
inst->shiftY = 0;
inst->shiftX = 0;
return (f0r_instance_t)inst;
}
@@ -120,7 +119,7 @@ void f0r_destruct(f0r_instance_t instance)
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 @@ void f0r_set_param_value(f0r_instance_t instance,
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 @@ void f0r_set_param_value(f0r_instance_t instance,
{
// 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 @@ void f0r_get_param_value(f0r_instance_t instance,
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 @@ void f0r_update(f0r_instance_t instance, double time,
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 @@ void f0r_update(f0r_instance_t instance, double time,
*(dst + x + (y*inst->width)) = (pxG | pxB | pxR);
}
}

View File

@@ -26,12 +26,16 @@ class sobel : public frei0r::filter
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<height-1; ++y)
{
@@ -46,9 +50,9 @@ public:
unsigned char *p7 = (unsigned char *)&in[(y+1)*width+(x-1)];
unsigned char *p8 = (unsigned char *)&in[(y+1)*width+x];
unsigned char *p9 = (unsigned char *)&in[(y+1)*width+(x+1)];
unsigned char *g = (unsigned char *)&out[y*width+x];
for (int i=0; i<3; ++i)
g[i] = CLAMP0255(
abs(p1[i] + p2[i]*2 + p3[i] - p7[i] - p8[i]*2 - p9[i]) +

View File

@@ -22,8 +22,27 @@
#include <stdlib.h>
#include <assert.h>
#ifdef __SSE4_1__
/* Check for SSE4.1 support */
#if defined(__SSE4_1__)
#include <smmintrin.h>
#define USE_SSE4_1 1
#else
#define USE_SSE4_1 0
#endif
/* Check for other SIMD instruction sets */
#if defined(__AVX__) && defined(__AVX2__)
#include <immintrin.h>
#define USE_AVX2 1
#else
#define USE_AVX2 0
#endif
#if defined(__ARM_NEON__) || defined(__ARM_NEON)
#include <arm_neon.h>
#define USE_NEON 1
#else
#define USE_NEON 0
#endif
#include <frei0r.h>
@@ -46,17 +65,17 @@ int f0r_init()
void f0r_deinit()
{ /* no initialization required */ }
void f0r_get_plugin_info(f0r_plugin_info_t* tint0r_instance_t)
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{
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";
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 @@ void f0r_get_param_value(f0r_instance_t instance,
}
}
#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)
#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)
{
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),
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--)
// Process pixels in groups of 4
for (size_t i = 0; i < len; i++)
{
#ifdef __SSE4_1__
/* 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 @@ void f0r_update(f0r_instance_t instance, double time,
p = _mm_packus_epi16(_mm_packus_epi32(p0, p1),
_mm_packus_epi32(p2, p3));
_mm_storeu_si128((__m128i*)outframe, p);
_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
/* Stride of 128 bits; i.e. 16 bytes */
inframe += 4;
outframe += 4;
#else
luma = (b * .114 + g * .587 + r * .299);
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;
*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
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
}

View File

@@ -1,7 +1,7 @@
/* Water filter
*
* (c) Copyright 2000-2007 Denis Rojo <jaromil@dyne.org>
*
* (c) Copyright 2000-2025 Denis Roio <jaromil@dyne.org>
*
* 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.
*
@@ -23,8 +23,6 @@
* 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 $"
*
*/
#include <stdio.h>
@@ -81,8 +79,9 @@ public:
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 @@ public:
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 @@ public:
BkGdImage = (uint32_t*) malloc(geo->size);
BkGdImagePost = (uint32_t*)malloc(geo->size);
}
// Initialize surface to NULL
surface = NULL;
}
~Water() {
@@ -152,7 +154,7 @@ public:
memcpy(BkGdImage, in, width*height*sizeof(uint32_t));
water_update(out);
}
private:
ScreenGeometry *geo;
@@ -162,11 +164,11 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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_bigsplash(int x, int y) {
}
void Water::water_surfer() {
int x, y;
x = (geo->w>>1)
+ ((
(
@@ -322,7 +325,7 @@ void Water::water_surfer() {
) >> 16);
xang += 13;
yang += 12;
if(mode & 0x4000)
{
offset = (oy+y)/2*geo->w + ((ox+x)>>1); // QUAAA
@@ -331,7 +334,7 @@ void Water::water_surfer() {
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 @@ void Water::water_surfer() {
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_swirl() {
void Water::water_3swirls() {
#define ANGLE 15
int x, y;
x = (95)
+ ((
(FCos(swirlangle)) * (ANGLE)
@@ -382,7 +387,7 @@ void Water::water_3swirls() {
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 @@ void Water::water_3swirls() {
+ ((
(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 @@ void Water::water_3swirls() {
+ ((
(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 @@ void Water::water_3swirls() {
/* 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(newoffset<maxoffset)
if (newoffset < maxoffset) {
out[offset] = BkGdImage[newoffset];
}
offset++;
dx = ptr[offset] - ptr[offset+1];
dy = ptr[offset] - ptr[offset+geo->w];
newoffset = offset + geo->w*(dy>>3) + (dx>>3);
if(newoffset<maxoffset)
if (newoffset < maxoffset) {
out[offset] = BkGdImage[newoffset];
}
}
}
}
@@ -443,10 +448,9 @@ void Water::CalcWater(int npage, int density) {
int count = geo->w + 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 @@ void Water::SmoothWater(int npage) {
int count = geo->w + 1;
int *newptr = (int*) &Height[npage][0];
int *oldptr = (int*) &Height[npage^1][0];
int x, y;
for(y=1; y<geo->h-1; y++) {
for(x=1; x<geo->w-1; x++) {
for(int y=1; y<geo->h-1; y++) {
for(int x=1; x<geo->w-1; x++) {
/* eight pixel */
newh = ((oldptr[count + geo->w]
+ oldptr[count - geo->w]
@@ -483,8 +486,8 @@ void Water::SmoothWater(int npage) {
+ oldptr[count + geo->w + 1]
) >> 3 )
+ newptr[count];
newptr[count] = newh>>1;
count++;
}
@@ -497,10 +500,9 @@ void Water::CalcWaterBigFilter(int npage, int density) {
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; y<geo->h-2; y++) {
for(x=2; x<geo->w-2; x++) {
for(int y=2; y<geo->h-2; y++) {
for(int x=2; x<geo->w-2; x++) {
/* 25 pixels */
newh = (
(
@@ -559,8 +561,13 @@ void Water::HeightBlob(int x, int y, int radius, int height, int page) {
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 @@ void Water::HeightBox (int x, int y, int radius, int height, int page) {
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 @@ void Water::WarpBlob(int x, int y, int radius, int height, int page) {
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 @@ void Water::SineBlob(int x, int y, int radius, int height, int page) {
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 @@ void Water::SineBlob(int x, int y, int radius, int height, int page) {
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;
}
}
}
}

View File

@@ -29,6 +29,9 @@ class blend : public frei0r::mixer2
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 @@ public:
const uint32_t* in1,
const uint32_t* in2)
{
// Validate inputs
if (!out || !in1 || !in2) {
return;
}
const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1);
const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2);
uint8_t *dst = reinterpret_cast<uint8_t*>(out);
@@ -52,12 +60,17 @@ public:
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;

View File

@@ -77,23 +77,42 @@ void f0r_get_param_info(f0r_param_info_t* info, int param_index)
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 @@ void f0r_set_param_value(f0r_instance_t instance, f0r_param_t param, int param_i
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 @@ void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_i
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;
}
}
@@ -135,27 +170,53 @@ void draw_composite(cairo_blend_instance_t* inst, unsigned char* out, unsigned c
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);
// 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);
}
// Set source, blen 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_update(f0r_instance_t instance, double time,
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 @@ void f0r_update2(f0r_instance_t instance, double time, const uint32_t* inframe1,
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);

View File

@@ -29,6 +29,9 @@ class overlay : public frei0r::mixer2
public:
overlay(unsigned int width, unsigned int height)
{
this->width = width;
this->height = height;
this->size = width * height;
}
/**
@@ -44,22 +47,32 @@ public:
const uint32_t* in1,
const uint32_t* in2)
{
// Validate inputs
if (!out || !in1 || !in2) {
return;
}
const uint8_t *src1 = reinterpret_cast<const uint8_t*>(in1);
const uint8_t *src2 = reinterpret_cast<const uint8_t*>(in2);
uint8_t *dst = reinterpret_cast<uint8_t*>(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;