Changeset View
Changeset View
Standalone View
Standalone View
source/blender/imbuf/intern/filter.c
| Show All 19 Lines | |||||
| /** \file | /** \file | ||||
| * \ingroup imbuf | * \ingroup imbuf | ||||
| */ | */ | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLI_math_base.h" | #include "BLI_math_base.h" | ||||
| #include "BLI_math_color.h" | |||||
| #include "BLI_math_vector.h" | |||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "IMB_filter.h" | #include "IMB_filter.h" | ||||
| #include "IMB_imbuf.h" | #include "IMB_imbuf.h" | ||||
| #include "IMB_imbuf_types.h" | #include "IMB_imbuf_types.h" | ||||
| #include "imbuf.h" | #include "imbuf.h" | ||||
| ▲ Show 20 Lines • Show All 358 Lines • ▼ Show 20 Lines | static int filter_make_index(const int x, const int y, const int w, const int h) | ||||
| if (x < 0 || x >= w || y < 0 || y >= h) { | if (x < 0 || x >= w || y < 0 || y >= h) { | ||||
| return -1; /* return bad index */ | return -1; /* return bad index */ | ||||
| } | } | ||||
| else { | else { | ||||
| return y * w + x; | return y * w + x; | ||||
| } | } | ||||
| } | } | ||||
| static int check_pixel_assigned( | static int check_pixel_assigned(const void *buffer, | ||||
| const void *buffer, const char *mask, const int index, const int depth, const bool is_float) | const char *mask, | ||||
| const int index, | |||||
| const int depth, | |||||
| const bool is_float, | |||||
| const bool preserve_existing) | |||||
| { | { | ||||
| int res = 0; | int res = 0; | ||||
| if (index >= 0) { | if (index >= 0) { | ||||
| const int alpha_index = depth * index + (depth - 1); | const int alpha_index = depth * index + (depth - 1); | ||||
| bool alpha_is_set; | |||||
| if (is_float) { | |||||
| alpha_is_set = ((const float *)buffer)[alpha_index] != 0.0f; | |||||
| } | |||||
| else { | |||||
| alpha_is_set = ((const unsigned char *)buffer)[alpha_index] != 0; | |||||
| } | |||||
| if (mask != NULL) { | if (mask != NULL) { | ||||
| res = mask[index] != 0 ? 1 : 0; | res = (mask[index] != 0 || (preserve_existing && alpha_is_set)) ? 1 : 0; | ||||
| } | } | ||||
| else if ((is_float && ((const float *)buffer)[alpha_index] != 0.0f) || | else if (alpha_is_set) { | ||||
| (!is_float && ((const unsigned char *)buffer)[alpha_index] != 0)) { | |||||
| res = 1; | res = 1; | ||||
| } | } | ||||
| } | } | ||||
| return res; | return res; | ||||
| } | } | ||||
| /** | /** | ||||
| * if alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0 | * if alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0 | ||||
| * | * | ||||
| * When a mask is given, only effect pixels with a mask value of 1, | * When a mask is given, only effect pixels with a mask value of 1, | ||||
| * defined as #BAKE_MASK_MARGIN in rendercore.c | * defined as #BAKE_MASK_MARGIN in rendercore.c | ||||
| * */ | * */ | ||||
| void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter) | void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter, bool preserve_existing) | ||||
| { | { | ||||
| const int width = ibuf->x; | const int width = ibuf->x; | ||||
| const int height = ibuf->y; | const int height = ibuf->y; | ||||
| const int depth = 4; /* always 4 channels */ | const int depth = 4; /* always 4 channels */ | ||||
| const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(unsigned char); | const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(unsigned char); | ||||
| const size_t bsize = ((size_t)width) * height * depth * chsize; | const size_t bsize = ((size_t)width) * height * depth * chsize; | ||||
| const bool is_float = (ibuf->rect_float != NULL); | const bool is_float = (ibuf->rect_float != NULL); | ||||
| void *dstbuf = (void *)MEM_dupallocN(ibuf->rect_float ? (void *)ibuf->rect_float : | void *dstbuf = (void *)MEM_dupallocN(ibuf->rect_float ? (void *)ibuf->rect_float : | ||||
| (void *)ibuf->rect); | (void *)ibuf->rect); | ||||
| char *dstmask = mask == NULL ? NULL : (char *)MEM_dupallocN(mask); | char *dstmask = mask == NULL ? NULL : (char *)MEM_dupallocN(mask); | ||||
| void *srcbuf = ibuf->rect_float ? (void *)ibuf->rect_float : (void *)ibuf->rect; | void *srcbuf = ibuf->rect_float ? (void *)ibuf->rect_float : (void *)ibuf->rect; | ||||
| char *srcmask = mask; | char *srcmask = mask; | ||||
| int cannot_early_out = 1, r, n, k, i, j, c; | const int neighbor_radius = 1; | ||||
| float weight[25]; | |||||
| /* build a weights buffer */ | |||||
| n = 1; | |||||
| #if 0 | |||||
| k = 0; | |||||
| for (i = -n; i <= n; i++) { | |||||
| for (j = -n; j <= n; j++) { | |||||
| weight[k++] = sqrt((float)i * i + j * j); | |||||
| } | |||||
| } | |||||
| #endif | |||||
| weight[0] = 1; | |||||
| weight[1] = 2; | |||||
| weight[2] = 1; | |||||
| weight[3] = 2; | |||||
| weight[4] = 0; | |||||
| weight[5] = 2; | |||||
| weight[6] = 1; | |||||
| weight[7] = 2; | |||||
| weight[8] = 1; | |||||
| /* run passes */ | /* run passes */ | ||||
| for (r = 0; cannot_early_out == 1 && r < filter; r++) { | for (int r = 0; r < filter; r++) { | ||||
| int x, y; | bool had_change = false; | ||||
| cannot_early_out = 0; | for (int y = 0; y < height; y++) { | ||||
| for (int x = 0; x < width; x++) { | |||||
| for (y = 0; y < height; y++) { | |||||
| for (x = 0; x < width; x++) { | |||||
| const int index = filter_make_index(x, y, width, height); | const int index = filter_make_index(x, y, width, height); | ||||
| /* only update unassigned pixels */ | /* only update unassigned pixels */ | ||||
| if (!check_pixel_assigned(srcbuf, srcmask, index, depth, is_float)) { | if (!check_pixel_assigned(srcbuf, srcmask, index, depth, is_float, preserve_existing)) { | ||||
| float tmp[4]; | float pixel[4]; | ||||
| float wsum = 0; | float wsum = 0; | ||||
| float acc[4] = {0, 0, 0, 0}; | float acc[4] = {0, 0, 0, 0}; | ||||
| k = 0; | |||||
| if (check_pixel_assigned( | /* Loop over all neighboring pixels, check if they are assigned and accumulate their | ||||
| srcbuf, srcmask, filter_make_index(x - 1, y, width, height), depth, is_float) || | * contributions. */ | ||||
| check_pixel_assigned( | bool had_direct_neighbor = false; | ||||
| srcbuf, srcmask, filter_make_index(x + 1, y, width, height), depth, is_float) || | for (int dy = -neighbor_radius; dy <= neighbor_radius; dy++) { | ||||
| check_pixel_assigned( | for (int dx = -neighbor_radius; dx <= neighbor_radius; dx++) { | ||||
| srcbuf, srcmask, filter_make_index(x, y - 1, width, height), depth, is_float) || | /* Skip the central pixel itself. */ | ||||
| check_pixel_assigned( | if (dx == 0 && dy == 0) { | ||||
| srcbuf, srcmask, filter_make_index(x, y + 1, width, height), depth, is_float)) { | continue; | ||||
| for (i = -n; i <= n; i++) { | } | ||||
| for (j = -n; j <= n; j++) { | |||||
| if (i != 0 || j != 0) { | const int neighbor_index = filter_make_index(x + dx, y + dy, width, height); | ||||
| const int tmpindex = filter_make_index(x + i, y + j, width, height); | |||||
| if (check_pixel_assigned(srcbuf, srcmask, tmpindex, depth, is_float)) { | if (check_pixel_assigned(srcbuf, srcmask, neighbor_index, depth, is_float, false)) { | ||||
| if (is_float) { | if (is_float) { | ||||
| for (c = 0; c < depth; c++) { | copy_v4_v4(pixel, ((float *)srcbuf) + depth * neighbor_index); | ||||
| tmp[c] = ((const float *)srcbuf)[depth * tmpindex + c]; | |||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| for (c = 0; c < depth; c++) { | rgba_uchar_to_float(pixel, ((unsigned char *)srcbuf) + depth * neighbor_index); | ||||
| tmp[c] = (float)((const unsigned char *)srcbuf)[depth * tmpindex + c]; | |||||
| } | |||||
| } | } | ||||
| wsum += weight[k]; | /* Check if this is a direct neighbor. */ | ||||
| float weight; | |||||
| for (c = 0; c < depth; c++) { | if (dx == 0 || dy == 0) { | ||||
| acc[c] += weight[k] * tmp[c]; | had_direct_neighbor = true; | ||||
| weight = 2.0f; | |||||
| } | } | ||||
| else { | |||||
| weight = 1.0f; | |||||
| } | } | ||||
| wsum += weight; | |||||
| madd_v4_v4fl(acc, pixel, weight); | |||||
| } | } | ||||
| k++; | |||||
| } | } | ||||
| } | } | ||||
| if (wsum != 0) { | /* If any direct neighbors were valid, assign the accumulated value to this pixel. | ||||
| for (c = 0; c < depth; c++) { | * We require a direct neighbor in order to get a diagonal corner in the extended border. | ||||
| acc[c] /= wsum; | */ | ||||
| } | if (had_direct_neighbor) { | ||||
| mul_v4_fl(acc, 1.0f / wsum); | |||||
| if (is_float) { | if (is_float) { | ||||
| for (c = 0; c < depth; c++) { | copy_v4_v4(((float *)dstbuf) + depth * index, acc); | ||||
| ((float *)dstbuf)[depth * index + c] = acc[c]; | |||||
| } | |||||
| } | } | ||||
| else { | else { | ||||
| for (c = 0; c < depth; c++) { | rgba_float_to_uchar(((unsigned char *)dstbuf) + depth * index, acc); | ||||
| ((unsigned char *)dstbuf)[depth * index + c] = | |||||
| acc[c] > 255 ? 255 : (acc[c] < 0 ? 0 : ((unsigned char)(acc[c] + 0.5f))); | |||||
| } | |||||
| } | } | ||||
| if (dstmask != NULL) { | if (dstmask != NULL) | ||||
| dstmask[index] = FILTER_MASK_MARGIN; /* assigned */ | dstmask[index] = FILTER_MASK_MARGIN; /* assigned */ | ||||
| } | |||||
| cannot_early_out = 1; | had_change = true; | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* keep the original buffer up to date. */ | /* keep the original buffer up to date. */ | ||||
| memcpy(srcbuf, dstbuf, bsize); | memcpy(srcbuf, dstbuf, bsize); | ||||
| if (dstmask != NULL) { | if (dstmask != NULL) { | ||||
| memcpy(srcmask, dstmask, ((size_t)width) * height); | memcpy(srcmask, dstmask, ((size_t)width) * height); | ||||
| } | } | ||||
| /* If no pixels changed during a pass, nothing will change in following passes either, so we | |||||
| * can exit early. */ | |||||
| if (!had_change) { | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| /* free memory */ | /* free memory */ | ||||
| MEM_freeN(dstbuf); | MEM_freeN(dstbuf); | ||||
| if (dstmask != NULL) { | if (dstmask != NULL) { | ||||
| MEM_freeN(dstmask); | MEM_freeN(dstmask); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 204 Lines • Show Last 20 Lines | |||||