#include "evas_engine_filter.h" #include #include // FIXME: Add proper stride support static int _box_blur_auto_radius(int *radii, int r) { if (r <= 2) { radii[0] = r; radii[1] = 0; WRN("Radius is too small for auto box blur: %d", r); return 1; } else if (r <= 6) { radii[0] = r / 2; radii[1] = r - radii[0] - 1; radii[2] = 0; XDBG("Using auto radius for %d: %d %d", r, radii[0], radii[1]); return 2; } else { radii[0] = (r + 3) / 3; radii[1] = (r + 2) / 3; radii[2] = r - radii[0] - radii[1]; radii[3] = 0; XDBG("Using auto radius for %d: %d %d %d", r, radii[0], radii[1], radii[2]); return 3; } } #include "./blur/blur_box_rgba_.c" #ifdef BUILD_MMX #include "./blur/blur_box_rgba_i386.c" #endif #ifdef BUILD_SSE3 #include "./blur/blur_box_rgba_sse3.c" #endif #ifdef BUILD_NEON #include "./blur/blur_box_rgba_neon.c" #endif static void _box_blur_horiz_rgba(const uint32_t *src, int src_stride, uint32_t *dst, int dst_stride, int* radii, Eina_Rectangle region) { DEBUG_TIME_BEGIN(); #ifdef BUILD_SSE3 if (eina_cpu_features_get() & EINA_CPU_SSE3) { _box_blur_rgba_horiz_step_sse3(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_MMX if (eina_cpu_features_get() & EINA_CPU_MMX) { _box_blur_rgba_horiz_step_mmx(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_NEON if (eina_cpu_features_get() & EINA_CPU_NEON) { _box_blur_rgba_horiz_step_neon(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif _box_blur_rgba_horiz_step(src, src_stride, dst, dst_stride, radii, region); #if defined(BUILD_SSE3) || defined(BUILD_MMX) || defined(BUILD_NEON) end: #endif DEBUG_TIME_END(); } static void _box_blur_vert_rgba(const uint32_t *src, int src_stride, uint32_t *dst, int dst_stride, int* radii, Eina_Rectangle region) { DEBUG_TIME_BEGIN(); #ifdef BUILD_SSE3 if (eina_cpu_features_get() & EINA_CPU_SSE3) { _box_blur_rgba_vert_step_sse3(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_MMX if (eina_cpu_features_get() & EINA_CPU_MMX) { _box_blur_rgba_vert_step_mmx(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_NEON if (eina_cpu_features_get() & EINA_CPU_NEON) { _box_blur_rgba_vert_step_neon(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif _box_blur_rgba_vert_step(src, src_stride, dst, dst_stride, radii, region); #if defined(BUILD_SSE3) || defined(BUILD_MMX) || defined(BUILD_NEON) end: #endif DEBUG_TIME_END(); } #include "./blur/blur_box_alpha_.c" #ifdef BUILD_MMX #include "./blur/blur_box_alpha_i386.c" #endif #ifdef BUILD_SSE3 #include "./blur/blur_box_alpha_sse3.c" #endif #ifdef BUILD_NEON #include "./blur/blur_box_alpha_neon.c" #endif static void _box_blur_horiz_alpha(const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int* radii, Eina_Rectangle region) { DEBUG_TIME_BEGIN(); #ifdef BUILD_SSE3 if (eina_cpu_features_get() & EINA_CPU_SSE3) { _box_blur_alpha_horiz_step_sse3(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_MMX if (eina_cpu_features_get() & EINA_CPU_MMX) { _box_blur_alpha_horiz_step_mmx(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_NEON if (eina_cpu_features_get() & EINA_CPU_NEON) { _box_blur_alpha_horiz_step_neon(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif _box_blur_alpha_horiz_step(src, src_stride, dst, dst_stride, radii, region); #if defined(BUILD_SSE3) || defined(BUILD_MMX) || defined(BUILD_NEON) end: #endif DEBUG_TIME_END(); } static void _box_blur_vert_alpha(const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, int* radii, Eina_Rectangle region) { DEBUG_TIME_BEGIN(); #ifdef BUILD_SSE3 if (eina_cpu_features_get() & EINA_CPU_SSE3) { _box_blur_alpha_vert_step_sse3(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_MMX if (eina_cpu_features_get() & EINA_CPU_MMX) { _box_blur_alpha_vert_step_mmx(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif #ifdef BUILD_NEON if (eina_cpu_features_get() & EINA_CPU_NEON) { _box_blur_alpha_vert_step_neon(src, src_stride, dst, dst_stride, radii, region); goto end; } #endif _box_blur_alpha_vert_step(src, src_stride, dst, dst_stride, radii, region); #if defined(BUILD_SSE3) || defined(BUILD_MMX) || defined(BUILD_NEON) end: #endif DEBUG_TIME_END(); } static inline Eina_Rectangle _rect(int x, int y, int w, int h, int maxw, int maxh) { Eina_Rectangle rect; if (x < 0) { w -= (-x); x = 0; } if (y < 0) { h -= (-y); y = 0; } if ((x + w) > maxw) w = maxw - x; if ((y + h) > maxh) h = maxh - y; if (w < 0) w = 0; if (h < 0) h = 0; rect.x = x; rect.y = y; rect.w = w; rect.h = h; return rect; } #define RECT(_x, _y, _w, _h) _rect(_x, _y, _w, _h, w, h) static Eina_Bool _box_blur_apply(Evas_Filter_Command *cmd, Eina_Bool vert, Eina_Bool rgba) { unsigned int src_len, src_stride, dst_len, dst_stride; Eina_Bool ret = EINA_FALSE; Eina_Rectangle o, region[4]; int radii[7] = {0}; int radius, regions, w, h; void *src, *dst; radius = abs(vert ? (int) cmd->blur.dy : (int) cmd->blur.dx); src = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, rgba ? E_ARGB : E_ALPHA, &src_stride); dst = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, rgba ? E_ARGB : E_ALPHA, &dst_stride); if (!src || !dst) goto unmap; if (cmd->blur.auto_count) _box_blur_auto_radius(radii, radius); else for (int k = 0; k < cmd->blur.count; k++) radii[k] = radius; w = cmd->input->w; h = cmd->input->h; o = cmd->ctx->obscured.effective; if (!o.w || !o.h) { region[0] = RECT(0, 0, w, h); regions = 1; } else if (!vert) { // top (full), left, right, bottom (full) region[0] = RECT(0, 0, w, o.y); region[1] = RECT(0, o.y, o.x, o.h); region[2] = RECT(o.x + o.w, o.y, w - o.x - o.w, o.h); region[3] = RECT(0, o.y + o.h, w, h - o.y - o.h); regions = 4; } else { // left (full), top, bottom, right (full) region[0] = RECT(0, 0, o.x, h); region[1] = RECT(o.x, 0, o.w, o.y); region[2] = RECT(o.x, o.y + o.h, o.w, h - o.y - o.h); region[3] = RECT(o.x + o.w, 0, w - o.x - o.w, h); regions = 4; } XDBG("Box blur on image %dx%d obscured by %d,%d %dx%d", w, h, o.x, o.y, o.w, o.h); for (int k = 0; k < regions; k++) { XDBG("Box blur in region %d,%d %dx%d", region[k].x, region[k].y, region[k].w, region[k].h); if (rgba) { if (!vert) _box_blur_horiz_rgba(src, src_stride / 4, dst, dst_stride / 4, radii, region[k]); else _box_blur_vert_rgba(src, src_stride / 4, dst, dst_stride / 4, radii, region[k]); } else { if (!vert) _box_blur_horiz_alpha(src, src_stride, dst, dst_stride, radii, region[k]); else _box_blur_vert_alpha(src, src_stride, dst, dst_stride, radii, region[k]); } } ret = EINA_TRUE; unmap: ector_buffer_unmap(cmd->input->buffer, src, src_len); ector_buffer_unmap(cmd->output->buffer, dst, dst_len); return ret; } static Eina_Bool _box_blur_horiz_apply_alpha(Evas_Filter_Command *cmd) { return _box_blur_apply(cmd, 0, 0); } static Eina_Bool _box_blur_vert_apply_alpha(Evas_Filter_Command *cmd) { return _box_blur_apply(cmd, 1, 0); } static Eina_Bool _box_blur_horiz_apply_rgba(Evas_Filter_Command *cmd) { return _box_blur_apply(cmd, 0, 1); } static Eina_Bool _box_blur_vert_apply_rgba(Evas_Filter_Command *cmd) { return _box_blur_apply(cmd, 1, 1); } /* Gaussian blur */ static void _sin_blur_weights_get(int *weights, int *pow2_divider, int radius) { const int diameter = 2 * radius + 1; double x, divider, sum = 0.0; double dweights[diameter]; int k, nextpow2, isum = 0; const int FAKE_PI = 3.0; /* Base curve: * f(x) = sin(x+pi/2)/2+1/2 */ for (k = 0; k < diameter; k++) { x = ((double) k / (double) (diameter - 1)) * FAKE_PI * 2.0 - FAKE_PI; dweights[k] = ((sin(x + M_PI_2) + 1.0) / 2.0) * 1024.0; sum += dweights[k]; } // Now we need to normalize to have a 2^N divider. nextpow2 = log2(2 * sum); divider = (double) (1 << nextpow2); for (k = 0; k < diameter; k++) { weights[k] = round(dweights[k] * divider / sum); isum += weights[k]; } // Final correction. The difference SHOULD be small... weights[radius] += (int) divider - isum; if (pow2_divider) *pow2_divider = nextpow2; } #define FUNCTION_NAME _gaussian_blur_horiz_alpha_step #define STEP 1 #include "./blur/blur_gaussian_alpha_.c" // Step size is w (row by row), loops = w, so STEP = 'loops' #define FUNCTION_NAME _gaussian_blur_vert_alpha_step #define STEP loops #include "./blur/blur_gaussian_alpha_.c" #define FUNCTION_NAME _gaussian_blur_horiz_rgba_step #define STEP 1 #include "./blur/blur_gaussian_rgba_.c" #define FUNCTION_NAME _gaussian_blur_vert_rgba_step #define STEP loops #include "./blur/blur_gaussian_rgba_.c" static Eina_Bool _gaussian_blur_apply(Evas_Filter_Command *cmd, Eina_Bool vert, Eina_Bool rgba) { unsigned int src_len, src_stride, dst_len, dst_stride, radius; Eina_Bool ret = EINA_TRUE; int pow2_div = 0, w, h; void *src, *dst; int *weights; radius = abs(vert ? (int) cmd->blur.dy : (int) cmd->blur.dx); src = _buffer_map_all(cmd->input->buffer, &src_len, E_READ, rgba ? E_ARGB : E_ALPHA, &src_stride); dst = _buffer_map_all(cmd->output->buffer, &dst_len, E_WRITE, rgba ? E_ARGB : E_ALPHA, &dst_stride); w = cmd->input->w; h = cmd->input->h; weights = alloca((2 * radius + 1) * sizeof(int)); _sin_blur_weights_get(weights, &pow2_div, radius); if (src && dst) { DEBUG_TIME_BEGIN(); if (rgba) { if (!vert) _gaussian_blur_horiz_rgba_step(src, dst, radius, w, h, w, weights, pow2_div); else _gaussian_blur_vert_rgba_step(src, dst, radius, h, w, 1, weights, pow2_div); } else { if (!vert) _gaussian_blur_horiz_alpha_step(src, dst, radius, w, h, w, weights, pow2_div); else _gaussian_blur_vert_alpha_step(src, dst, radius, h, w, 1, weights, pow2_div); } DEBUG_TIME_END(); } else ret = EINA_FALSE; ector_buffer_unmap(cmd->input->buffer, src, src_len); ector_buffer_unmap(cmd->output->buffer, dst, dst_len); return ret; } static Eina_Bool _gaussian_blur_horiz_apply_alpha(Evas_Filter_Command *cmd) { return _gaussian_blur_apply(cmd, 0, 0); } static Eina_Bool _gaussian_blur_vert_apply_alpha(Evas_Filter_Command *cmd) { return _gaussian_blur_apply(cmd, 1, 0); } static Eina_Bool _gaussian_blur_horiz_apply_rgba(Evas_Filter_Command *cmd) { return _gaussian_blur_apply(cmd, 0, 1); } static Eina_Bool _gaussian_blur_vert_apply_rgba(Evas_Filter_Command *cmd) { return _gaussian_blur_apply(cmd, 1, 1); } /* Main entry point */ Software_Filter_Func eng_filter_blur_func_get(Evas_Filter_Command *cmd) { EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL); EINA_SAFETY_ON_FALSE_RETURN_VAL(cmd->mode == EVAS_FILTER_MODE_BLUR, NULL); switch (cmd->blur.type) { case EVAS_FILTER_BLUR_BOX: if (!cmd->output->alpha_only) { if (cmd->blur.dx) return _box_blur_horiz_apply_rgba; else if (cmd->blur.dy) return _box_blur_vert_apply_rgba; } else { if (cmd->blur.dx) return _box_blur_horiz_apply_alpha; else if (cmd->blur.dy) return _box_blur_vert_apply_alpha; } break; case EVAS_FILTER_BLUR_GAUSSIAN: if (!cmd->output->alpha_only) { if (cmd->blur.dx) return _gaussian_blur_horiz_apply_rgba; else if (cmd->blur.dy) return _gaussian_blur_vert_apply_rgba; } else { if (cmd->blur.dx) return _gaussian_blur_horiz_apply_alpha; else if (cmd->blur.dy) return _gaussian_blur_vert_apply_alpha; } break; default: ERR("Unsupported blur type %d", cmd->blur.type); return NULL; } return NULL; }