blob: 9a63ac61171991b4c3f722e371e984515afd5188 [file] [log] [blame]
#pragma version(1)
#pragma rs java_package_name(com.android.rs.refocus.renderscript)
#pragma rs_fp_relaxed
// This is a speedup version of layered_filter_f32.rs using summation table.
// Several kernel functions and global functions for refocus rendering using
// Render Script. These functions are accessible from Java side. The assumptions
// made in this file are (consistent with Java side):
//
// 1. The depth value varies between 1 and number of depth levels. 0 is reserved
// for invalid pixels, e.g., padded pixels around image boundary.
// 2. The depth value represents inverse depth. Larger depths are closer to the
// camera.
// 3. The depth values are grouped into several blending layers.
// 4. The focal layer (i.e., the global variable g_focal_layer) is the depth
// value interval that has no blur.
//
// For the following kernel functions defined in this rs file, the input
// arguments have the following meaning:
//
// @param in an input RGBD pixel with @in.a being quantized inverse depth.
// @param x x-coordinate of @param in in the unpadded input image.
// @param y y-coordinate of @param in in the unpadded input image.
#include "camera_response.rsh"
#include "layer_info_fast.rsh"
#include "luts_for_speedup_f32.rsh"
#include "layered_filter_d1new_helper.rsh"
// Set force CPU variable for kernels:
// UnpackInputImage, ComputeLayerMatteBehindFocalDepth, ComputeIntegralImageForLayerBehindFocalDepth,
// FilterLayerBehindFocalDepth, updateSharpImageUsingFuzzyImage, ComputeLayerMatteInFrontOfFocalDepth,
// FilterLayerInFrontOfFocalDepth
// Image size of padded images: g_sharp_image and g_fuzzy_image.
// Float4 that stores the image size info: 1. width, 2. height, 3. margin
static int4 g_image_size_i4;
static CameraResponse_t g_camera_response;
// This image buffer is used to store input image initially.
// Then the input image gets updated as each layer is processed from the
// back-most to the focal depth. This image buffer is padded.
// This image buffer is used to save an integral image of g_sharp_image
// modulated by the visibility mask.
// whether or not to use integral image in this iteration.
static int g_use_integral_image;
// This image buffer is used to store output image, both for intermediate
// results and final result.
// In the first pass from back-most to focal depth, g_fuzzy_image holds the
// layer filtering result temporarily and then the result is used to update the
// input image after each layer is processed.
// In the second pass from front-most to focal depth, g_fuzzy_image accumulates
// all the layer filtering results and in the end, the accumulation result is
// blended with the input image (which has been updated in the first pass) to
// generate the final result. This image buffer is padded.
// The dilation radius that controls how the current layer should be blended
// with previous layers.
static BlendInfo_t g_blend_info;
// The depth level that is in focus.
// int2 that stores the focal layer info: 1. front_depth, 2. back_depth
static int2 g_focal_layer_i2;
// This is an allocation to store kernel info: 1. offset, 2. radius_x, 3. radius_y
rs_allocation galloc_kernel_info;
// Precomputed LUTs for speedup.
static VisibilityProbability_t g_visibility_probability;
static SecantOffset_t g_secant_offset;
static const float g_kOneOver255 = 1.0f / 255.0f;
static const float g_kAlmostOne = .998f;
// (1 << g_kDepthScaleShift) must be the same as BlurStack::DEPTH_SCALE.
static const int g_kDepthScaleShift = 2;
// g_kMaxDepth must be the same as BlurStack::MAX_DETPH.
static const int g_kMaxDepth = 256 >> g_kDepthScaleShift;
// Copies an input (unpadded) RGBD image into g_sharp_image, which has been
// padded with margin. Initialize other fields.
void __attribute__((kernel))
UnpackInputImage(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
float4 sharp_RGBA;
sharp_RGBA.r =
ApplyLUT_Float(in.r * g_kOneOver255, g_camera_response.lut_remove_crf_float);
sharp_RGBA.g =
ApplyLUT_Float(in.g * g_kOneOver255, g_camera_response.lut_remove_crf_float);
sharp_RGBA.b =
ApplyLUT_Float(in.b * g_kOneOver255, g_camera_response.lut_remove_crf_float);
rsSetElementAt_float4(g_sharp_RGBA, sharp_RGBA, index);
rsSetElementAt_uchar(g_sharp_actual_depth, g_kMaxDepth - (in.a >> g_kDepthScaleShift), index);
}
// Marks active pixels that are on the target layer.
// Initializes the matte of active pixels to be the dilation_radius+1, which is
// equivalent to 1.
// Initializes dilated_depth of active pixels and pixels that are close to
// active pixels to be actual depths.
void __attribute__((kernel)) MarkLayerMask(uchar4 in, uint32_t x, uint32_t y) {
const int actual_depth = g_kMaxDepth - (in.a >> g_kDepthScaleShift);
if (!OnTheLayer(actual_depth, g_target_layer_i2)) return;
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
// Marks this pixel as active.
rsSetElementAt_uchar(g_sharp_active, 1, index);
rsSetElementAt_uchar(g_sharp_matte, g_blend_info.dilation_radius + 1, index);
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index);
rsSetElementAt_uchar(g_sharp_dilated_depth, sharp_actual_depth, index);
// Next, tries to figure out whether or not this pixel is on the boundary
// between active and inactive pixels
int is_this_pixel_on_boundary = 0;
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index - g_image_size_i4.s0);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index + g_image_size_i4.s0);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index - 1);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index + 1);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
if (!is_this_pixel_on_boundary) {
return;
}
// Marks pixels near the boundary of active pixels to compute matte later.
const int kernel_center_x = g_blend_info.dilation_radius;
const int kernel_center_y = g_blend_info.dilation_radius;
const int kernel_dim_x = 2 * kernel_center_x + 1;
// index of the current meta data, initailly set to top left sharp meta data
// Moves sharp_nbr to the top left corner of this pixel.
int current_meta_index = x - (kernel_center_y * g_image_size_i4.s0 + kernel_center_x); // width
const int jump_to_next_pixel = 1;
const int jump_to_next_row = g_image_size_i4.s0 - kernel_dim_x; // width
for (int j = -kernel_center_y; j <= kernel_center_y; ++j) {
// Initializes dilated_depth as actual_depth.
// The dilated_depth will then be updated in distance transform later.
// A valid, non-zero dilated_depth indicates distance transform is
// needed for the pixel. Otherwise, the distance transform will be
// skipped.
for (int i = -kernel_center_x; i <= kernel_center_x; ++i) {
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, current_meta_index);
rsSetElementAt_uchar(g_sharp_dilated_depth, sharp_actual_depth, current_meta_index);
current_meta_index += jump_to_next_pixel;
}
current_meta_index += jump_to_next_row;
}
}
// Another version of MarkLayerMask kernel that directly passes input allocation to kernels
// Input: g_sharp_actual_depth
// Output: g_sharp_dilated_depth
void __attribute__((kernel)) MarkLayerMaskPassInput(uchar in_sharp_actual_depth, uint32_t x) {
if (!OnTheLayer(in_sharp_actual_depth, g_target_layer_i2)) return;
// Marks this pixel as active.
rsSetElementAt_uchar(g_sharp_active, 1, x);
rsSetElementAt_uchar(g_sharp_matte, g_blend_info.dilation_radius + 1, x);
// Next, tries to figure out whether or not this pixel is on the boundary
// between active and inactive pixels
int is_this_pixel_on_boundary = 0;
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x - g_image_size_i4.s0);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x + g_image_size_i4.s0);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x - 1);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x + 1);
is_this_pixel_on_boundary |=
ValidDepthNotOnTheLayer(sharp_actual_depth, g_target_layer_i2);
if (!is_this_pixel_on_boundary) {
rsSetElementAt_uchar(g_sharp_dilated_depth, in_sharp_actual_depth, x);
return;
}
// Marks pixels near the boundary of active pixels to compute matte later.
const int kernel_center_x = g_blend_info.dilation_radius;
const int kernel_center_y = g_blend_info.dilation_radius;
const int kernel_dim_x = 2 * kernel_center_x + 1;
// index of the current meta data, initailly set to top left sharp meta data
// Moves sharp_nbr to the top left corner of this pixel.
int current_meta_index = x - (kernel_center_y * g_image_size_i4.s0 + kernel_center_x); // width
const int jump_to_next_pixel = 1;
const int jump_to_next_row = g_image_size_i4.s0 - kernel_dim_x; // width
for (int j = -kernel_center_y; j <= kernel_center_y; ++j) {
// Initializes dilated_depth as actual_depth.
// The dilated_depth will then be updated in distance transform later.
// A valid, non-zero dilated_depth indicates distance transform is
// needed for the pixel. Otherwise, the distance transform will be
// skipped.
for (int i = -kernel_center_x; i <= kernel_center_x; ++i) {
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, current_meta_index);
rsSetElementAt_uchar(g_sharp_dilated_depth, sharp_actual_depth, current_meta_index);
current_meta_index += jump_to_next_pixel;
}
current_meta_index += jump_to_next_row;
}
}
// Distance transform in processing layers in pass one from the back-most to
// the sharp depth.
void __attribute__((kernel))
ComputeLayerMatteBehindFocalDepth(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
uchar sharp_active = rsGetElementAt_uchar(g_sharp_active, index);
uchar sharp_dilated_depth = rsGetElementAt_uchar(g_sharp_dilated_depth, index);
if (sharp_active == 0 && sharp_dilated_depth) {
// This pixel is not active but within the dilation radius of the active
// pixels.
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index);
if (NotInFrontOfTheLayer(sharp_actual_depth, g_target_layer_i2)) {
// We do not need to compute matte for depths in front of this layer,
// because these pixels will be over-written later and hence we don't need
// to blend them now.
Alloc_ComputeLayerMatteHelper(sharp_actual_depth, index, g_image_size_i4,
g_blend_info.dilation_radius);
}
}
}
// Pass input allocation directly to kernel ComputeLayerMatteBehindFocalDepth
// Input: g_sharp_dilated_depth
// Output: g_sharp_dilated_depth
uchar __attribute__((kernel))
ComputeLayerMatteBehindFocalDepthPassInput(uchar in_sharp_dilated_depth, uint32_t x) {
uchar sharp_active = rsGetElementAt_uchar(g_sharp_active, x);
if (sharp_active == 0 && in_sharp_dilated_depth) {
// This pixel is not active but within the dilation radius of the active
// pixels.
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x);
if (NotInFrontOfTheLayer(sharp_actual_depth, g_target_layer_i2)) {
// We do not need to compute matte for depths in front of this layer,
// because these pixels will be over-written later and hence we don't need
// to blend them now.
return ComputeLayerMattePassInputHelper(sharp_actual_depth, x, g_image_size_i4,
g_blend_info.dilation_radius);
}
}
return in_sharp_dilated_depth;
}
// Distance transform in processing layers in pass two from the front-most to
// the sharp depth.
void __attribute__((kernel))
ComputeLayerMatteInFrontOfFocalDepth(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
uchar sharp_active = rsGetElementAt_uchar(g_sharp_active, index);
uchar sharp_dilated_depth = rsGetElementAt_uchar(g_sharp_dilated_depth, index);
if (sharp_active == 0 && sharp_dilated_depth) {
// This pixel is not active but within the dilation radius of the active
// pixels.
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
if (fuzzy_RGBA.a < g_kAlmostOne) {
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index);
Alloc_ComputeLayerMatteHelper(sharp_actual_depth, index, g_image_size_i4,
g_blend_info.dilation_radius);
}
}
}
// Pass input allocation directly into kernel ComputeLayerMatteInFrontOfFocalDepth
// Input: g_sharp_dilated_depth
// Output: g_sharp_dilated_depth
uchar __attribute__((kernel))
ComputeLayerMatteInFrontOfFocalDepthPassInput(uchar in_sharp_dilated_depth, uint32_t x) {
uchar sharp_active = rsGetElementAt_uchar(g_sharp_active, x);
if (sharp_active == 0 && in_sharp_dilated_depth) {
// This pixel is not active but within the dilation radius of the active
// pixels.
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, x);
if (fuzzy_RGBA.a < g_kAlmostOne) {
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, x);
return ComputeLayerMattePassInputHelper(sharp_actual_depth,x, g_image_size_i4,
g_blend_info.dilation_radius);
}
}
return in_sharp_dilated_depth;
}
// Computes integral image for target layer in processing layers in pass one
// from the back-most to the sharp depth.
void __attribute__((kernel))
ComputeIntegralImageForLayerBehindFocalDepth(uchar4 in, uint32_t x,
uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
// Kernel invocation should make sure x = 0.
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0; // width
float4 sharp_RGBA;
float4 integral_RGBA;
uchar sharp_actual_depth;
int current_index = index;
int last_index = index + g_image_size_i4.s0; // width
// Gets the visibility probability lookup table for the target layer depth.
const float *vis_prob =
g_visibility_probability.lut[g_target_layer_i2.s0];
float4 prev_integral_value = 0;
for(; current_index != last_index; current_index++) {
sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, current_index);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, current_index);
const float weight = vis_prob[sharp_actual_depth];
float4 this_value = {weight * sharp_RGBA.r, weight * sharp_RGBA.g,
weight * sharp_RGBA.b, weight};
prev_integral_value = prev_integral_value + this_value;
// update pointer image
integral_RGBA = prev_integral_value;
rsSetElementAt_float4(g_integral_RGBA, integral_RGBA, current_index);
}
}
void __attribute__((kernel))
ComputeIntegralImageForLayerInFrontOfFocalDepth(uchar4 in, uint32_t x,
uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
// Kernel invocation should make sure x = 0.
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0; // width
float4 sharp_RGBA;
float4 integral_RGBA;
uchar sharp_actual_depth;
int current_index = index;
int last_index = index + g_image_size_i4.s0; // width
float4 prev_integral_value = 0;
for(; current_index != last_index; current_index++) {
sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, current_index);
sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, current_index);
const float weight = ValidDepth(sharp_actual_depth);
float4 this_value = {weight * sharp_RGBA.r, weight * sharp_RGBA.g,
weight * sharp_RGBA.b, weight};
prev_integral_value = prev_integral_value + this_value;
// Update image
integral_RGBA = prev_integral_value;
rsSetElementAt_float4(g_integral_RGBA, integral_RGBA, current_index);
}
}
// Filters target layer in processing layers in pass one from the back-most to
// the sharp depth.
void __attribute__((kernel))
FilterLayerBehindFocalDepth(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
uchar matte = rsGetElementAt_uchar(g_sharp_matte, index);
if (matte == 0) {
return;
}
// At this point, sharp->dilated_depth must be within the depth range of
// this target layer. Hence, the kernel_info below is a valid pointer.
uchar sharp_dilated_depth = rsGetElementAt_uchar(g_sharp_dilated_depth, index);
const float4 alloc_kernel_info = rsGetElementAt_float4(galloc_kernel_info, sharp_dilated_depth - g_target_layer_i2.s1);
float4 result;
// Saves the filtering result in g_fuzzy_image temporarily.
if (g_use_integral_image) {
result = AllocFilterLayerUsingRowwiseIntegralImage(
x, y, g_image_size_i4, alloc_kernel_info, &g_secant_offset);
} else {
result = AllocFilterLayerBehindFocalDepthHelper(
x, y, g_image_size_i4, g_target_layer_i2,
alloc_kernel_info, &g_visibility_probability);
}
rsSetElementAt_float4(g_fuzzy_RGBA, result, index);
// Once the kernel invoke is completed, uses the g_fuzzy_image to update
// g_sharp_image.
}
// Directly pass allocation to kernel FilterLayerBehindFocalDepth
// Input: g_fuzzy_RGBA
// Output: g_fuzzy_RGBA
float4 __attribute__((kernel))
FilterLayerBehindFocalDepthPassInput(float4 in_fuzzy_RGBA, uint32_t x) {
uchar sharp_matte = rsGetElementAt_uchar(g_sharp_matte, x);
if (sharp_matte == 0) {
return in_fuzzy_RGBA;
}
// At this point, sharp->dilated_depth must be within the depth range of
// this target layer. Hence, the kernel_info below is a valid pointer.
uchar sharp_dilated_depth = rsGetElementAt_uchar(g_sharp_dilated_depth, x);
const float4 alloc_kernel_info = rsGetElementAt_float4(galloc_kernel_info, sharp_dilated_depth - g_target_layer_i2.s1);
float4 result;
// Saves the filtering result in g_fuzzy_image temporarily.
if (g_use_integral_image) {
result = AllocFilterLayerUsingRowwiseIntegralImage(
x, 0, g_image_size_i4, alloc_kernel_info, &g_secant_offset);
} else {
result = AllocFilterLayerBehindFocalDepthHelper(
x, 0, g_image_size_i4, g_target_layer_i2,
alloc_kernel_info, &g_visibility_probability);
}
return result;
// Once the kernel invoke is completed, uses the g_fuzzy_image to update
// g_sharp_image.
}
// Filters target layer in processing layers in pass two from the front-most
// to the focus depth.
void __attribute__((kernel))
FilterLayerInFrontOfFocalDepth(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
uchar depth = rsGetElementAt_uchar(g_sharp_dilated_depth, index);
if (depth == 0) {
return;
}
// At this point, this pixel must be either active or close enough to an
// active pixel.
// For such a pixel, its matte value can still be zero if its distance
// transform to the active pixels is larger than dilation_radius.
uchar matte = rsGetElementAt_uchar(g_sharp_matte, index);
// Resets fields of this pixel to prepare for the next iteration.
// Does not reset sharp->actual_depth, which will be used in future
// iterations.
rsSetElementAt_uchar(g_sharp_active, 0, index);
rsSetElementAt_uchar(g_sharp_matte, 0, index);
rsSetElementAt_uchar(g_sharp_dilated_depth, 0, index);
if (matte == 0) {
return;
}
// At this point, sharp->dilated_depth must be within the depth range of
// this target layer. Hence kernel_info below is a valid pointer.
const float4 alloc_kernel_info = rsGetElementAt_float4(galloc_kernel_info, depth - g_target_layer_i2.s1);
float4 result;
if (g_use_integral_image) {
result = AllocFilterLayerUsingRowwiseIntegralImage(
x, y, g_image_size_i4, alloc_kernel_info, &g_secant_offset);
} else {
result = AllocFilterLayerInFrontOfFocalDepthHelper(
x, y, g_image_size_i4, alloc_kernel_info);
}
// Because matte !=0 here, fuzzy->a < g_kAlmostOne must be true.
// Otherwise, ComputeLayerMatteHelper won't be called in
// ComputeLayerMatteInFrontOfFocalDepth, which results in a zero matte.
// Accumulates the filtering result to fuzzy.
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
const float capacity = 1.0f - fuzzy_RGBA.a;
const float factor = capacity * (float)matte * g_blend_info.matte_normalizer;
fuzzy_RGBA.r += factor * result.r;
fuzzy_RGBA.g += factor * result.g;
fuzzy_RGBA.b += factor * result.b;
fuzzy_RGBA.a += factor;
rsSetElementAt_float4(g_fuzzy_RGBA, fuzzy_RGBA, index);
}
// Pass input allocation directly to kernel FilterLayerInFrontOfFocalDepth
// Input: g_sharp_dilated_depth
// Output: g_sharp_dilated_depth
uchar __attribute__((kernel))
FilterLayerInFrontOfFocalDepthPassInput(uchar in_sharp_dilated_depth, uint32_t x) {
if (in_sharp_dilated_depth == 0) {
return 0;
}
// At this point, this pixel must be either active or close enough to an
// active pixel.
// For such a pixel, its matte value can still be zero if its distance
// transform to the active pixels is larger than dilation_radius.
uchar matte = rsGetElementAt_uchar(g_sharp_matte, x);
// Resets fields of this pixel to prepare for the next iteration.
// Does not reset sharp->actual_depth, which will be used in future
// iterations.
rsSetElementAt_uchar(g_sharp_active, 0, x);
rsSetElementAt_uchar(g_sharp_matte, 0, x);
if (matte == 0) {
return 0;
}
// At this point, sharp->dilated_depth must be within the depth range of
// this target layer. Hence kernel_info below is a valid pointer.
const float4 alloc_kernel_info = rsGetElementAt_float4(galloc_kernel_info, in_sharp_dilated_depth - g_target_layer_i2.s1);
float4 result;
if (g_use_integral_image) {
result = AllocFilterLayerUsingRowwiseIntegralImage(
x, 0, g_image_size_i4, alloc_kernel_info, &g_secant_offset);
} else {
result = AllocFilterLayerInFrontOfFocalDepthHelper(
x, 0, g_image_size_i4, alloc_kernel_info);
}
// Because matte !=0 here, fuzzy->a < g_kAlmostOne must be true.
// Otherwise, ComputeLayerMatteHelper won't be called in
// ComputeLayerMatteInFrontOfFocalDepth, which results in a zero matte.
// Accumulates the filtering result to fuzzy.
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, x);
const float capacity = 1.0f - fuzzy_RGBA.a;
const float factor = capacity * (float)matte * g_blend_info.matte_normalizer;
fuzzy_RGBA.r += factor * result.r;
fuzzy_RGBA.g += factor * result.g;
fuzzy_RGBA.b += factor * result.b;
fuzzy_RGBA.a += factor;
rsSetElementAt_float4(g_fuzzy_RGBA, fuzzy_RGBA, x);
return 0;
}
// Replaces active pixels in g_sharp_image with the filtering result saved in
// g_fuzzy_image. Does the replacement in a soft way by blending g_sharp_image
// and g_fuzzy_image using the matte field.
void __attribute__((kernel))
UpdateSharpImageUsingFuzzyImage(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
uchar depth = rsGetElementAt_uchar(g_sharp_dilated_depth, index);
if (depth == 0) {
return;
}
// At this point, this pixel must be either active or close enough to an
// active pixel.
// For such a pixel, its matte value can still be zero if its distance
// transform to the active pixels is larger than dilation_radius.
// Resets fields of this pixel to prepare for future layer processing (pass
// two).
uchar matte = rsGetElementAt_uchar(g_sharp_matte, index);
rsSetElementAt_uchar(g_sharp_active, 0, index);
rsSetElementAt_uchar(g_sharp_matte, 0, index);
rsSetElementAt_uchar(g_sharp_dilated_depth, 0, index);
// Does not reset sharp->actual depth, which will be used in future
// layer processing.
if (matte == 0) {
return;
}
float factor = (float)matte * g_blend_info.matte_normalizer;
// The following blending amounts to:
// sharp = (1 - factor) * sharp + factor * fuzzy.
float4 sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, index);
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
sharp_RGBA.r += factor * (fuzzy_RGBA.r - sharp_RGBA.r);
sharp_RGBA.g += factor * (fuzzy_RGBA.g - sharp_RGBA.g);
sharp_RGBA.b += factor * (fuzzy_RGBA.b - sharp_RGBA.b);
fuzzy_RGBA.r = 0;
fuzzy_RGBA.g = 0;
fuzzy_RGBA.b = 0;
fuzzy_RGBA.a = 0;
rsSetElementAt_float4(g_sharp_RGBA, sharp_RGBA, index);
rsSetElementAt_float4(g_fuzzy_RGBA, fuzzy_RGBA, index);
}
// Passes allocation for g_sharp_meta from java code
// so that one rsGetElement is eliminated.
// Still requires set and get for g_fuzzy_RGBA and g_sharp_RGBA
// Input: g_sharp_dilated_depth
// Output: g_sharp_dilated_depth
uchar __attribute__((kernel))
UpdateSharpUsingFuzzyPassInput(uchar in_sharp_dilated_depth, uint32_t x) {
// Variable in is the uchar4 for sharp_meta
if (in_sharp_dilated_depth == 0) {
return 0;
}
// At this point, this pixel must be either active or close enough to an
// active pixel.
// For such a pixel, its matte value can still be zero if its distance
// transform to the active pixels is larger than dilation_radius.
// Resets fields of this pixel to prepare for future layer processing (pass
// two).
// Index of g_sharp_meta should align with g_fuzzy_RGBA and g_sharp_RGBA
uchar matte = rsGetElementAt_uchar(g_sharp_matte, x);
rsSetElementAt_uchar(g_sharp_active, 0, x);
rsSetElementAt_uchar(g_sharp_matte, 0, x);
// Does not reset sharp->actual depth, which will be used in future
// layer processing.
if (matte == 0) {
return 0;
}
float factor = (float)matte * g_blend_info.matte_normalizer;
// The following blending amounts to:
// sharp = (1 - factor) * sharp + factor * fuzzy.
float4 sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, x);
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, x);
sharp_RGBA.r += factor * (fuzzy_RGBA.r - sharp_RGBA.r);
sharp_RGBA.g += factor * (fuzzy_RGBA.g - sharp_RGBA.g);
sharp_RGBA.b += factor * (fuzzy_RGBA.b - sharp_RGBA.b);
fuzzy_RGBA.r = 0;
fuzzy_RGBA.g = 0;
fuzzy_RGBA.b = 0;
fuzzy_RGBA.a = 0;
rsSetElementAt_float4(g_sharp_RGBA, sharp_RGBA, x);
rsSetElementAt_float4(g_fuzzy_RGBA, fuzzy_RGBA, x);
return 0;
}
// Fills in the pixels on or behind the focal depth in g_fuzzy_image using
// pixels in g_sharp_image. Does the filling in a soft way by blending using the
// matte field.
void __attribute__((kernel))
FinalizeFuzzyImageUsingSharpImage(uchar4 in, uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
const int index = y * g_image_size_i4.s0 + x; // width
float4 sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, index);
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
uchar sharp_actual_depth = rsGetElementAt_uchar(g_sharp_actual_depth, index);
if (sharp_actual_depth && sharp_actual_depth <= g_focal_layer_i2.s0) { // g_focal_layer.front_depth
float capacity = 1.0f - fuzzy_RGBA.a;
fuzzy_RGBA.r += sharp_RGBA.r * capacity;
fuzzy_RGBA.g += sharp_RGBA.g * capacity;
fuzzy_RGBA.b += sharp_RGBA.b * capacity;
fuzzy_RGBA.a = 1.0f;
rsSetElementAt_float4(g_fuzzy_RGBA, fuzzy_RGBA, index);
}
}
// Pass allocation directly to kernel FinalizeFuzzyImageUsingSharpImage
// Input: g_sharp_actual_depth
// Output: g_fuzzy_RGBA
float4 __attribute__((kernel))
FinalizeFuzzyImageUsingSharpImagePassInput(uchar in_sharp_actual_depth, uint32_t x) {
float4 sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, x);
float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, x);
if (in_sharp_actual_depth && in_sharp_actual_depth <= g_focal_layer_i2.s0) { // g_focal_layer.front_depth
float capacity = 1.0f - fuzzy_RGBA.a;
fuzzy_RGBA.r += sharp_RGBA.r * capacity;
fuzzy_RGBA.g += sharp_RGBA.g * capacity;
fuzzy_RGBA.b += sharp_RGBA.b * capacity;
fuzzy_RGBA.a = 1.0f;
return fuzzy_RGBA;
}
return rsGetElementAt_float4(g_fuzzy_RGBA, x);
}
// Copies g_fuzzy_image to output color image, excluding the padded margin.
// (x, y) is the pixel coordinate in the output image.
uchar4 __attribute__((kernel)) PackOutputImage(uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
// Returns the pixel at (x,y) after applying CRF.
const int index = y * g_image_size_i4.s0 + x; // width
const float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
float4 result;
result.r = ApplyLUT_Float(fuzzy_RGBA.r, g_camera_response.lut_apply_crf_float);
result.g = ApplyLUT_Float(fuzzy_RGBA.g, g_camera_response.lut_apply_crf_float);
result.b = ApplyLUT_Float(fuzzy_RGBA.b, g_camera_response.lut_apply_crf_float);
result.a = fuzzy_RGBA.a;
return rsPackColorTo8888(result);
}
// Copies g_fuzzy_image to output color image, excluding the padded margin.
// (x, y) is the pixel coordinate in the output image.
uchar4 __attribute__((kernel)) PackSharpImage(uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
// Returns the pixel at (x,y) after applying CRF.
const int index = y * g_image_size_i4.s0 + x; // width
const float4 sharp_RGBA = rsGetElementAt_float4(g_sharp_RGBA, index);
float4 result;
result.r = ApplyLUT_Float(sharp_RGBA.r, g_camera_response.lut_apply_crf_float);
result.g = ApplyLUT_Float(sharp_RGBA.g, g_camera_response.lut_apply_crf_float);
result.b = ApplyLUT_Float(sharp_RGBA.b, g_camera_response.lut_apply_crf_float);
return rsPackColorTo8888(result);
}
// Copies g_fuzzy_image to output color image, excluding the padded margin.
// (x, y) is the pixel coordinate in the output image.
uchar4 __attribute__((kernel)) PackFuzzyImage(uint32_t x, uint32_t y) {
// Maps (x,y) to the padded image coordinate system.
x += g_image_size_i4.s2; // margin
y += g_image_size_i4.s2; // margin
// Returns the pixel at (x,y) after applying CRF.
const int index = y * g_image_size_i4.s0 + x; // width
const float4 fuzzy_RGBA = rsGetElementAt_float4(g_fuzzy_RGBA, index);
float4 result;
result.r = ApplyLUT_Float(fuzzy_RGBA.r, g_camera_response.lut_apply_crf_float);
result.g = ApplyLUT_Float(fuzzy_RGBA.g, g_camera_response.lut_apply_crf_float);
result.b = ApplyLUT_Float(fuzzy_RGBA.b, g_camera_response.lut_apply_crf_float);
result.a = fuzzy_RGBA.a;
return rsPackColorTo8888(result);
}
void SetTargetLayer(int front, int back) {
g_target_layer_i2.s0 = front;
g_target_layer_i2.s1 = back;
}
void SetBlendInfo(int radius) {
g_blend_info.dilation_radius = radius;
g_blend_info.matte_normalizer = 1.0f / (radius + 1);
}
void SetUseIntegralImage(int use_integral_image) {
g_use_integral_image = use_integral_image;
}
void InitializeFast(int width, int height, int margin, int front, int back) {
// Initialize image size
g_image_size_i4.s0 = width;
g_image_size_i4.s1 = height;
g_image_size_i4.s2 = margin;
// Initialize focal layer
g_focal_layer_i2.s0 = front;
g_focal_layer_i2.s1 = back;
const int num_pixels = width * height;
for(int i = 0; i < num_pixels; i++) {
float4 zerof4 = {0.0f, 0.0f, 0.0f, 0.0f};
rsSetElementAt_float4(g_sharp_RGBA, zerof4, i);
rsSetElementAt_float4(g_fuzzy_RGBA, zerof4, i);
rsSetElementAt_float4(g_integral_RGBA, zerof4, i);
rsSetElementAt_uchar(g_sharp_actual_depth, 0, i);
rsSetElementAt_uchar(g_sharp_active, 0, i);
rsSetElementAt_uchar(g_sharp_matte, 0, i);
rsSetElementAt_uchar(g_sharp_dilated_depth, 0, i);
}
InitializeDefaultCameraResponse(&g_camera_response);
InitializeRadiusOffset(&g_secant_offset);
InitializeVisibilityProbability(&g_visibility_probability, front, back);
}