/** * Perform the actual accumulation along a ray segment from source to pt. * Only pixels withing dist_min..dist_max contribute. * * The loop runs backwards(!) over the primary sector space axis u, i.e. increasing distance to pt. * After each step it decrements v by dv < 1, adding a buffer shift when necessary. */ static void eval(MemoryBuffer *input, float output[4], const float pt_ofs[2], const float source[2], float dist_min, float dist_max) { rcti rect = *input->getRect(); int buffer_width = input->getWidth(); int x, y, num; float v, dv; float falloff_factor; /* initialise the iteration variables */ float *buffer = init_buffer_iterator(input, source, pt_ofs, dist_min, dist_max, x, y, num, v, dv, falloff_factor); int tot = 0; /* v_local keeps track of when to decrement v (see below) */ float v_local = v - floorf(v); for (int i = 0; i < num; i++) { /* range check, abort when running beyond the image border */ if (x < rect.xmin || x >= rect.xmax || y < rect.ymin || y >= rect.ymax) break; float f = 1.0f - (float)i * falloff_factor; madd_v4_v4fl(output, buffer, buffer[3] * f * f); /* TODO implement proper filtering here, see * http://en.wikipedia.org/wiki/Lanczos_resampling * http://en.wikipedia.org/wiki/Sinc_function * * using lanczos with x = distance from the line segment, * normalized to a == 0.5f, could give a good result * * for now just count samples and divide equally at the end ... */ tot++; /* decrement u */ x -= fxx; y -= fyx; buffer -= (fxx + fyx * buffer_width) * COM_NUMBER_OF_CHANNELS; /* decrement v (in steps of dv < 1) */ v_local -= dv; if (v_local < 0.0f) { v_local += 1.0f; x -= fxy; y -= fyy; buffer -= (fxy + fyy * buffer_width) * COM_NUMBER_OF_CHANNELS; } } /* normalize */ if (num > 0) { mul_v4_fl(output, 1.0f / (float)num); } }
static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool is_torus) { int x, y, count, xi, yi, xo, yo; int out_off[2], in_off[2], dim[2]; float outrgb[4]; dim[0] = ibufb->x; dim[1] = ibufb->y; in_off[0] = pos[0]; in_off[1] = pos[1]; out_off[0] = out_off[1] = 0; if (!is_torus) { IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); if ((dim[0] == 0) || (dim[1] == 0)) return; } for (y = 0; y < dim[1]; y++) { for (x = 0; x < dim[0]; x++) { /* get input pixel */ xi = in_off[0] + x; yi = in_off[1] + y; count = 1; paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); mul_v4_fl(outrgb, 1.0f / (float)count); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); } } }
static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w) { float inrgb[4]; if (tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); /* need to also do clipping here always since tiled coordinates * are not always within bounds */ if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb); } else return 0; mul_v4_fl(inrgb, w); add_v4_v4(outrgb, inrgb); return w; }
static float paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus, float w) { float inrgb[4]; // XXX: signed unsigned mismatch if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) { if (torus) paint_2d_ibuf_rgb_get(ibuf, x, y, 1, inrgb); else return 0; } else { paint_2d_ibuf_rgb_get(ibuf, x, y, 0, inrgb); } mul_v4_fl(inrgb, w); add_v4_v4(outrgb, inrgb); return w; }
void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler /*sampler*/) { float uv[2]; float deriv[2][2]; if (this->m_motion_blur_samples == 1) { warpCoord(x, y, this->m_samples[0].perspectiveMatrix, uv, deriv); m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); } else { zero_v4(output); for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) { float color[4]; warpCoord(x, y, this->m_samples[sample].perspectiveMatrix, uv, deriv); m_pixelReader->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); add_v4_v4(output, color); } mul_v4_fl(output, 1.0f / (float)this->m_motion_blur_samples); } }
/** * Filtering method based on * "Creating raster omnimax images from multiple perspective views using the elliptical weighted average filter" * by Ned Greene and Paul S. Heckbert (1986) */ void MemoryBuffer::readEWA(float result[4], const float uv[2], const float derivatives[2][2], PixelSampler sampler) { zero_v4(result); int width = this->getWidth(), height = this->getHeight(); if (width == 0 || height == 0) return; float u = uv[0], v = uv[1]; float Ux = derivatives[0][0], Vx = derivatives[1][0], Uy = derivatives[0][1], Vy = derivatives[1][1]; float A, B, C, F, ue, ve; ellipse_params(Ux, Uy, Vx, Vy, A, B, C, F, ue, ve); /* Note: highly eccentric ellipses can lead to large texture space areas to filter! * This is limited somewhat by the EWA_WTS size in the loop, but a nicer approach * could be the one found in * "High Quality Elliptical Texture Filtering on GPU" * by Pavlos Mavridis and Georgios Papaioannou * in which the eccentricity of the ellipse is clamped. */ int U0 = (int)u; int V0 = (int)v; /* pixel offset for interpolation */ float ufac = u - floorf(u), vfac = v - floorf(v); /* filter size */ int u1 = (int)(u - ue); int u2 = (int)(u + ue); int v1 = (int)(v - ve); int v2 = (int)(v + ve); /* sane clamping to avoid unnecessarily huge loops */ /* note: if eccentricity gets clamped (see above), * the ue/ve limits can also be lowered accordingly */ if (U0 - u1 > EWA_MAXIDX) u1 = U0 - EWA_MAXIDX; if (u2 - U0 > EWA_MAXIDX) u2 = U0 + EWA_MAXIDX; if (V0 - v1 > EWA_MAXIDX) v1 = V0 - EWA_MAXIDX; if (v2 - V0 > EWA_MAXIDX) v2 = V0 + EWA_MAXIDX; float DDQ = 2.0f * A; float U = u1 - U0; float ac1 = A * (2.0f * U + 1.0f); float ac2 = A * U * U; float BU = B * U; float sum = 0.0f; for (int v = v1; v <= v2; ++v) { float V = v - V0; float DQ = ac1 + B * V; float Q = (C * V + BU) * V + ac2; for (int u = u1; u <= u2; ++u) { if (Q < F) { float tc[4]; const float wt = EWA_WTS[CLAMPIS((int)Q, 0, EWA_MAXIDX)]; switch (sampler) { case COM_PS_NEAREST: read(tc, u, v); break; case COM_PS_BILINEAR: readBilinear(tc, (float)u + ufac, (float)v + vfac); break; case COM_PS_BICUBIC: readBilinear(tc, (float)u + ufac, (float)v + vfac); break; /* XXX no readBicubic method yet */ default: zero_v4(tc); break; } madd_v4_v4fl(result, tc, wt); sum += wt; } Q += DQ; DQ += DDQ; } } mul_v4_fl(result, (sum != 0.0f ? 1.0f / sum : 0.0f)); }
static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) { bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); float threshold = s->brush->sharp_threshold; int x, y, xi, yi, xo, yo, xk, yk; float count; int out_off[2], in_off[2], dim[2]; int diff_pos[2]; float outrgb[4]; float rgba[4]; BlurKernel *kernel = s->blurkernel; dim[0] = ibufb->x; dim[1] = ibufb->y; in_off[0] = pos[0]; in_off[1] = pos[1]; out_off[0] = out_off[1] = 0; if (!tile) { IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); if ((dim[0] == 0) || (dim[1] == 0)) return; } /* find offset inside mask buffers to sample them */ sub_v2_v2v2_int(diff_pos, out_off, in_off); for (y = 0; y < dim[1]; y++) { for (x = 0; x < dim[0]; x++) { /* get input pixel */ xi = in_off[0] + x; yi = in_off[1] + y; count = 0.0; if (tile) { paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); else zero_v4(rgba); } else { /* coordinates have been clipped properly here, it should be safe to do this */ paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); } zero_v4(outrgb); for (yk = 0; yk < kernel->side; yk++) { for (xk = 0; xk < kernel->side; xk++) { count += paint_2d_ibuf_add_if(ibuf, xi + xk - kernel->pixel_len, yi + yk - kernel->pixel_len, outrgb, tile, kernel->wdata[xk + yk * kernel->side]); } } if (count > 0.0f) { mul_v4_fl(outrgb, 1.0f / (float)count); if (sharpen) { /* subtract blurred image from normal image gives high pass filter */ sub_v3_v3v3(outrgb, rgba, outrgb); /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid * colored speckles appearing in final image, and also to check for threshold */ outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); if (fabsf(outrgb[0]) > threshold) { float mask = BKE_brush_alpha_get(s->scene, s->brush); float alpha = rgba[3]; rgba[3] = outrgb[3] = mask; /* add to enhance edges */ blend_color_add_float(outrgb, rgba, outrgb); outrgb[3] = alpha; } else copy_v4_v4(outrgb, rgba); } } else copy_v4_v4(outrgb, rgba); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); } } }
void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*data*/) { float w = 0.0f; float color_org[4]; float color_mid[4]; float color_mid_ok[4]; float in1[4]; int x1 = x - 1; int x2 = x; int x3 = x + 1; int y1 = y - 1; int y2 = y; int y3 = y + 1; CLAMP(x1, 0, getWidth() - 1); CLAMP(x2, 0, getWidth() - 1); CLAMP(x3, 0, getWidth() - 1); CLAMP(y1, 0, getHeight() - 1); CLAMP(y2, 0, getHeight() - 1); CLAMP(y3, 0, getHeight() - 1); float value[4]; this->m_inputValueOperation->read(value, x2, y2, NULL); // const float mval = 1.0f - value[0]; this->m_inputOperation->read(color_org, x2, y2, NULL); #define TOT_DIV_ONE 1.0f #define TOT_DIV_CNR (float)M_SQRT1_2 #define WTOT (TOT_DIV_ONE * 4 + TOT_DIV_CNR * 4) #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ if (color_diff(in1, color_org, this->m_threshold)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ } zero_v4(color_mid); zero_v4(color_mid_ok); this->m_inputOperation->read(in1, x1, y1, NULL); COLOR_ADD(TOT_DIV_CNR) this->m_inputOperation->read(in1, x2, y1, NULL); COLOR_ADD(TOT_DIV_ONE) this->m_inputOperation->read(in1, x3, y1, NULL); COLOR_ADD(TOT_DIV_CNR) this->m_inputOperation->read(in1, x1, y2, NULL); COLOR_ADD(TOT_DIV_ONE) #if 0 this->m_inputOperation->read(in2, x2, y2, NULL); madd_v4_v4fl(color_mid, in2, this->m_filter[4]); #endif this->m_inputOperation->read(in1, x3, y2, NULL); COLOR_ADD(TOT_DIV_ONE) this->m_inputOperation->read(in1, x1, y3, NULL); COLOR_ADD(TOT_DIV_CNR) this->m_inputOperation->read(in1, x2, y3, NULL); COLOR_ADD(TOT_DIV_ONE) this->m_inputOperation->read(in1, x3, y3, NULL); COLOR_ADD(TOT_DIV_CNR) mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); if ((w != 0.0f) && ((w / WTOT) > (this->m_threshold_neighbor)) && color_diff(color_mid, color_org, this->m_threshold)) { mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(output, color_org, color_mid_ok, value[0]); } else { copy_v4_v4(output, color_org); } }