Files
test2/source/blender/blenkernel/intern/mask_rasterize.c
Sergey Sharybin a12a8a71bb Remove "All Rights Reserved" from Blender Foundation copyright code
The goal is to solve confusion of the "All rights reserved" for licensing
code under an open-source license.

The phrase "All rights reserved" comes from a historical convention that
required this phrase for the copyright protection to apply. This convention
is no longer relevant.

However, even though the phrase has no meaning in establishing the copyright
it has not lost meaning in terms of licensing.

This change makes it so code under the Blender Foundation copyright does
not use "all rights reserved". This is also how the GPL license itself
states how to apply it to the source code:

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software ...

This change does not change copyright notice in cases when the copyright
is dual (BF and an author), or just an author of the code. It also does
mot change copyright which is inherited from NaN Holding BV as it needs
some further investigation about what is the proper way to handle it.
2023-03-30 10:51:59 +02:00

1471 lines
49 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2012 Blender Foundation */
/** \file
* \ingroup bke
*
* This module exposes a rasterizer that works as a black box - implementation details
* are confined to this file.
*
* The basic method to access is:
* - create & initialize a handle from a #Mask datablock.
* - execute pixel lookups.
* - free the handle.
*
* This file is admittedly a bit confusticated,
* in quite few areas speed was chosen over readability,
* though it is commented - so shouldn't be so hard to see what's going on.
*
* Implementation:
*
* To rasterize the mask its converted into geometry that use a ray-cast for each pixel lookup.
*
* Initially 'kdopbvh' was used but this ended up being too slow.
*
* To gain some extra speed we take advantage of a few shortcuts
* that can be made rasterizing masks specifically.
*
* - All triangles are known to be completely white -
* so no depth check is done on triangle intersection.
* - All quads are known to be feather outlines -
* the 1 and 0 depths are known by the vertex order in the quad,
* - There is no color - just a value for each mask pixel.
* - The mask spacial structure always maps to space 0-1 on X and Y axis.
* - Bucketing is used to speed up lookups for geometry.
*
* Other Details:
* - used unsigned values all over for some extra speed on some arch's.
* - anti-aliasing is faked, just ensuring at least one pixel feather - avoids oversampling.
* - initializing the spacial structure doesn't need to be as optimized as pixel lookups are.
* - mask lookups need not be pixel aligned so any sub-pixel values from x/y (0 - 1), can be found.
* (perhaps masks can be used as a vector texture in 3D later on)
* Currently, to build the spacial structure we have to calculate
* the total number of faces ahead of time.
*
* This is getting a bit complicated with the addition of unfilled splines and end capping -
* If large changes are needed here we would be better off using an iterable
* BLI_mempool for triangles and converting to a contiguous array afterwards.
*
* - Campbell
*/
#include "CLG_log.h"
#include "MEM_guardedalloc.h"
#include "DNA_mask_types.h"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
#include "BLI_memarena.h"
#include "BLI_scanfill.h"
#include "BLI_utildefines.h"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_task.h"
#include "BKE_mask.h"
#include "BLI_strict_flags.h"
/* this is rather and annoying hack, use define to isolate it.
* problem is caused by scanfill removing edges on us. */
#define USE_SCANFILL_EDGE_WORKAROUND
#define SPLINE_RESOL_CAP_PER_PIXEL 2
#define SPLINE_RESOL_CAP_MIN 8
#define SPLINE_RESOL_CAP_MAX 64
/* found this gives best performance for high detail masks, values between 2 and 8 work best */
#define BUCKET_PIXELS_PER_CELL 4
#define SF_EDGE_IS_BOUNDARY 0xff
#define SF_KEYINDEX_TEMP_ID ((uint)-1)
#define TRI_TERMINATOR_ID ((uint)-1)
#define TRI_VERT ((uint)-1)
/* for debugging add... */
#ifndef NDEBUG
// printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]);
# define FACE_ASSERT(face, vert_max) \
{ \
uint *_t = face; \
BLI_assert(_t[0] < vert_max); \
BLI_assert(_t[1] < vert_max); \
BLI_assert(_t[2] < vert_max); \
BLI_assert(_t[3] < vert_max || _t[3] == TRI_VERT); \
} \
(void)0
#else
/* do nothing */
# define FACE_ASSERT(face, vert_max)
#endif
static CLG_LogRef LOG = {"bke.mask_rasterize"};
static void rotate_point_v2(
float r_p[2], const float p[2], const float cent[2], const float angle, const float asp[2])
{
const float s = sinf(angle);
const float c = cosf(angle);
float p_new[2];
/* translate point back to origin */
r_p[0] = (p[0] - cent[0]) / asp[0];
r_p[1] = (p[1] - cent[1]) / asp[1];
/* rotate point */
p_new[0] = ((r_p[0] * c) - (r_p[1] * s)) * asp[0];
p_new[1] = ((r_p[0] * s) + (r_p[1] * c)) * asp[1];
/* translate point back */
r_p[0] = p_new[0] + cent[0];
r_p[1] = p_new[1] + cent[1];
}
BLI_INLINE uint clampis_uint(const uint v, const uint min, const uint max)
{
return v < min ? min : (v > max ? max : v);
}
static ScanFillVert *scanfill_vert_add_v2_with_depth(ScanFillContext *sf_ctx,
const float co_xy[2],
const float co_z)
{
const float co[3] = {co_xy[0], co_xy[1], co_z};
return BLI_scanfill_vert_add(sf_ctx, co);
}
/* --------------------------------------------------------------------- */
/* local structs for mask rasterizing */
/* --------------------------------------------------------------------- */
/**
* A single #MaskRasterHandle contains multiple #MaskRasterLayer's,
* each #MaskRasterLayer does its own lookup which contributes to
* the final pixel with its own blending mode and the final pixel
* is blended between these.
*
* \note internal use only.
*/
typedef struct MaskRasterLayer {
/* geometry */
uint face_tot;
uint (*face_array)[4]; /* access coords tri/quad */
float (*face_coords)[3]; /* xy, z 0-1 (1.0 == filled) */
/* 2d bounds (to quickly skip bucket lookup) */
rctf bounds;
/* buckets */
uint **buckets_face;
/* cache divide and subtract */
float buckets_xy_scalar[2]; /* (1.0 / (buckets_width + FLT_EPSILON)) * buckets_x */
uint buckets_x;
uint buckets_y;
/* copied direct from #MaskLayer.--- */
/* blending options */
float alpha;
char blend;
char blend_flag;
char falloff;
} MaskRasterLayer;
typedef struct MaskRasterSplineInfo {
/* body of the spline */
uint vertex_offset;
uint vertex_total;
/* capping for non-filled, non cyclic splines */
uint vertex_total_cap_head;
uint vertex_total_cap_tail;
bool is_cyclic;
} MaskRasterSplineInfo;
/**
* opaque local struct for mask pixel lookup, each MaskLayer needs one of these
*/
struct MaskRasterHandle {
MaskRasterLayer *layers;
uint layers_tot;
/* 2d bounds (to quickly skip bucket lookup) */
rctf bounds;
};
/* --------------------------------------------------------------------- */
/* alloc / free functions */
/* --------------------------------------------------------------------- */
MaskRasterHandle *BKE_maskrasterize_handle_new(void)
{
MaskRasterHandle *mr_handle;
mr_handle = MEM_callocN(sizeof(MaskRasterHandle), "MaskRasterHandle");
return mr_handle;
}
void BKE_maskrasterize_handle_free(MaskRasterHandle *mr_handle)
{
const uint layers_tot = mr_handle->layers_tot;
MaskRasterLayer *layer = mr_handle->layers;
for (uint i = 0; i < layers_tot; i++, layer++) {
if (layer->face_array) {
MEM_freeN(layer->face_array);
}
if (layer->face_coords) {
MEM_freeN(layer->face_coords);
}
if (layer->buckets_face) {
const uint bucket_tot = layer->buckets_x * layer->buckets_y;
uint bucket_index;
for (bucket_index = 0; bucket_index < bucket_tot; bucket_index++) {
uint *face_index = layer->buckets_face[bucket_index];
if (face_index) {
MEM_freeN(face_index);
}
}
MEM_freeN(layer->buckets_face);
}
}
MEM_freeN(mr_handle->layers);
MEM_freeN(mr_handle);
}
static void maskrasterize_spline_differentiate_point_outset(float (*diff_feather_points)[2],
float (*diff_points)[2],
const uint tot_diff_point,
const float ofs,
const bool do_test)
{
uint k_prev = tot_diff_point - 2;
uint k_curr = tot_diff_point - 1;
uint k_next = 0;
uint k;
float d_prev[2];
float d_next[2];
float d[2];
const float *co_prev;
const float *co_curr;
const float *co_next;
const float ofs_squared = ofs * ofs;
co_prev = diff_points[k_prev];
co_curr = diff_points[k_curr];
co_next = diff_points[k_next];
/* precalc */
sub_v2_v2v2(d_prev, co_prev, co_curr);
normalize_v2(d_prev);
for (k = 0; k < tot_diff_point; k++) {
/* co_prev = diff_points[k_prev]; */ /* precalc */
co_curr = diff_points[k_curr];
co_next = diff_points[k_next];
// sub_v2_v2v2(d_prev, co_prev, co_curr); /* precalc */
sub_v2_v2v2(d_next, co_curr, co_next);
// normalize_v2(d_prev); /* precalc */
normalize_v2(d_next);
if ((do_test == false) ||
(len_squared_v2v2(diff_feather_points[k], diff_points[k]) < ofs_squared)) {
add_v2_v2v2(d, d_prev, d_next);
normalize_v2(d);
diff_feather_points[k][0] = diff_points[k][0] + (d[1] * ofs);
diff_feather_points[k][1] = diff_points[k][1] + (-d[0] * ofs);
}
/* use next iter */
copy_v2_v2(d_prev, d_next);
/* k_prev = k_curr; */ /* precalc */
k_curr = k_next;
k_next++;
}
}
/* this function is not exact, sometimes it returns false positives,
* the main point of it is to clear out _almost_ all bucket/face non-intersections,
* returning true in corner cases is ok but missing an intersection is NOT.
*
* method used
* - check if the center of the buckets bounding box is intersecting the face
* - if not get the max radius to a corner of the bucket and see how close we
* are to any of the triangle edges.
*/
static bool layer_bucket_isect_test(const MaskRasterLayer *layer,
uint face_index,
const uint bucket_x,
const uint bucket_y,
const float bucket_size_x,
const float bucket_size_y,
const float bucket_max_rad_squared)
{
uint *face = layer->face_array[face_index];
float(*cos)[3] = layer->face_coords;
const float xmin = layer->bounds.xmin + (bucket_size_x * (float)bucket_x);
const float ymin = layer->bounds.ymin + (bucket_size_y * (float)bucket_y);
const float xmax = xmin + bucket_size_x;
const float ymax = ymin + bucket_size_y;
const float cent[2] = {(xmin + xmax) * 0.5f, (ymin + ymax) * 0.5f};
if (face[3] == TRI_VERT) {
const float *v1 = cos[face[0]];
const float *v2 = cos[face[1]];
const float *v3 = cos[face[2]];
if (isect_point_tri_v2(cent, v1, v2, v3)) {
return true;
}
if ((dist_squared_to_line_segment_v2(cent, v1, v2) < bucket_max_rad_squared) ||
(dist_squared_to_line_segment_v2(cent, v2, v3) < bucket_max_rad_squared) ||
(dist_squared_to_line_segment_v2(cent, v3, v1) < bucket_max_rad_squared)) {
return true;
}
// printf("skip tri\n");
return false;
}
const float *v1 = cos[face[0]];
const float *v2 = cos[face[1]];
const float *v3 = cos[face[2]];
const float *v4 = cos[face[3]];
if (isect_point_tri_v2(cent, v1, v2, v3)) {
return true;
}
if (isect_point_tri_v2(cent, v1, v3, v4)) {
return true;
}
if ((dist_squared_to_line_segment_v2(cent, v1, v2) < bucket_max_rad_squared) ||
(dist_squared_to_line_segment_v2(cent, v2, v3) < bucket_max_rad_squared) ||
(dist_squared_to_line_segment_v2(cent, v3, v4) < bucket_max_rad_squared) ||
(dist_squared_to_line_segment_v2(cent, v4, v1) < bucket_max_rad_squared)) {
return true;
}
// printf("skip quad\n");
return false;
}
static void layer_bucket_init_dummy(MaskRasterLayer *layer)
{
layer->face_tot = 0;
layer->face_coords = NULL;
layer->face_array = NULL;
layer->buckets_x = 0;
layer->buckets_y = 0;
layer->buckets_xy_scalar[0] = 0.0f;
layer->buckets_xy_scalar[1] = 0.0f;
layer->buckets_face = NULL;
BLI_rctf_init(&layer->bounds, -1.0f, -1.0f, -1.0f, -1.0f);
}
static void layer_bucket_init(MaskRasterLayer *layer, const float pixel_size)
{
MemArena *arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__);
const float bucket_dim_x = BLI_rctf_size_x(&layer->bounds);
const float bucket_dim_y = BLI_rctf_size_y(&layer->bounds);
layer->buckets_x = (uint)((bucket_dim_x / pixel_size) / (float)BUCKET_PIXELS_PER_CELL);
layer->buckets_y = (uint)((bucket_dim_y / pixel_size) / (float)BUCKET_PIXELS_PER_CELL);
// printf("bucket size %ux%u\n", layer->buckets_x, layer->buckets_y);
CLAMP(layer->buckets_x, 8, 512);
CLAMP(layer->buckets_y, 8, 512);
layer->buckets_xy_scalar[0] = (1.0f / (bucket_dim_x + FLT_EPSILON)) * (float)layer->buckets_x;
layer->buckets_xy_scalar[1] = (1.0f / (bucket_dim_y + FLT_EPSILON)) * (float)layer->buckets_y;
{
/* width and height of each bucket */
const float bucket_size_x = (bucket_dim_x + FLT_EPSILON) / (float)layer->buckets_x;
const float bucket_size_y = (bucket_dim_y + FLT_EPSILON) / (float)layer->buckets_y;
const float bucket_max_rad = (max_ff(bucket_size_x, bucket_size_y) * (float)M_SQRT2) +
FLT_EPSILON;
const float bucket_max_rad_squared = bucket_max_rad * bucket_max_rad;
uint *face = &layer->face_array[0][0];
float(*cos)[3] = layer->face_coords;
const uint bucket_tot = layer->buckets_x * layer->buckets_y;
LinkNode **bucketstore = MEM_callocN(bucket_tot * sizeof(LinkNode *), __func__);
uint *bucketstore_tot = MEM_callocN(bucket_tot * sizeof(uint), __func__);
uint face_index;
for (face_index = 0; face_index < layer->face_tot; face_index++, face += 4) {
float xmin;
float xmax;
float ymin;
float ymax;
if (face[3] == TRI_VERT) {
const float *v1 = cos[face[0]];
const float *v2 = cos[face[1]];
const float *v3 = cos[face[2]];
xmin = min_ff(v1[0], min_ff(v2[0], v3[0]));
xmax = max_ff(v1[0], max_ff(v2[0], v3[0]));
ymin = min_ff(v1[1], min_ff(v2[1], v3[1]));
ymax = max_ff(v1[1], max_ff(v2[1], v3[1]));
}
else {
const float *v1 = cos[face[0]];
const float *v2 = cos[face[1]];
const float *v3 = cos[face[2]];
const float *v4 = cos[face[3]];
xmin = min_ff(v1[0], min_ff(v2[0], min_ff(v3[0], v4[0])));
xmax = max_ff(v1[0], max_ff(v2[0], max_ff(v3[0], v4[0])));
ymin = min_ff(v1[1], min_ff(v2[1], min_ff(v3[1], v4[1])));
ymax = max_ff(v1[1], max_ff(v2[1], max_ff(v3[1], v4[1])));
}
/* not essential but may as will skip any faces outside the view */
if (!((xmax < 0.0f) || (ymax < 0.0f) || (xmin > 1.0f) || (ymin > 1.0f))) {
CLAMP(xmin, 0.0f, 1.0f);
CLAMP(ymin, 0.0f, 1.0f);
CLAMP(xmax, 0.0f, 1.0f);
CLAMP(ymax, 0.0f, 1.0f);
{
uint xi_min = (uint)((xmin - layer->bounds.xmin) * layer->buckets_xy_scalar[0]);
uint xi_max = (uint)((xmax - layer->bounds.xmin) * layer->buckets_xy_scalar[0]);
uint yi_min = (uint)((ymin - layer->bounds.ymin) * layer->buckets_xy_scalar[1]);
uint yi_max = (uint)((ymax - layer->bounds.ymin) * layer->buckets_xy_scalar[1]);
void *face_index_void = POINTER_FROM_UINT(face_index);
uint xi, yi;
/* this should _almost_ never happen but since it can in extreme cases,
* we have to clamp the values or we overrun the buffer and crash */
if (xi_min >= layer->buckets_x) {
xi_min = layer->buckets_x - 1;
}
if (xi_max >= layer->buckets_x) {
xi_max = layer->buckets_x - 1;
}
if (yi_min >= layer->buckets_y) {
yi_min = layer->buckets_y - 1;
}
if (yi_max >= layer->buckets_y) {
yi_max = layer->buckets_y - 1;
}
for (yi = yi_min; yi <= yi_max; yi++) {
uint bucket_index = (layer->buckets_x * yi) + xi_min;
for (xi = xi_min; xi <= xi_max; xi++, bucket_index++) {
/* correct but do in outer loop */
// uint bucket_index = (layer->buckets_x * yi) + xi;
BLI_assert(xi < layer->buckets_x);
BLI_assert(yi < layer->buckets_y);
BLI_assert(bucket_index < bucket_tot);
/* Check if the bucket intersects with the face. */
/* NOTE: there is a trade off here since checking box/tri intersections isn't as
* optimal as it could be, but checking pixels against faces they will never
* intersect with is likely the greater slowdown here -
* so check if the cell intersects the face. */
if (layer_bucket_isect_test(layer,
face_index,
xi,
yi,
bucket_size_x,
bucket_size_y,
bucket_max_rad_squared)) {
BLI_linklist_prepend_arena(&bucketstore[bucket_index], face_index_void, arena);
bucketstore_tot[bucket_index]++;
}
}
}
}
}
}
if (1) {
/* Now convert link-nodes into arrays for faster per pixel access. */
uint **buckets_face = MEM_mallocN(bucket_tot * sizeof(*buckets_face), __func__);
uint bucket_index;
for (bucket_index = 0; bucket_index < bucket_tot; bucket_index++) {
if (bucketstore_tot[bucket_index]) {
uint *bucket = MEM_mallocN((bucketstore_tot[bucket_index] + 1) * sizeof(uint), __func__);
LinkNode *bucket_node;
buckets_face[bucket_index] = bucket;
for (bucket_node = bucketstore[bucket_index]; bucket_node;
bucket_node = bucket_node->next) {
*bucket = POINTER_AS_UINT(bucket_node->link);
bucket++;
}
*bucket = TRI_TERMINATOR_ID;
}
else {
buckets_face[bucket_index] = NULL;
}
}
layer->buckets_face = buckets_face;
}
MEM_freeN(bucketstore);
MEM_freeN(bucketstore_tot);
}
BLI_memarena_free(arena);
}
void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle,
struct Mask *mask,
const int width,
const int height,
const bool do_aspect_correct,
const bool do_mask_aa,
const bool do_feather)
{
const rctf default_bounds = {0.0f, 1.0f, 0.0f, 1.0f};
const float pixel_size = 1.0f / (float)min_ii(width, height);
const float asp_xy[2] = {
(do_aspect_correct && width > height) ? (float)height / (float)width : 1.0f,
(do_aspect_correct && width < height) ? (float)width / (float)height : 1.0f};
const float zvec[3] = {0.0f, 0.0f, -1.0f};
MaskLayer *masklay;
uint masklay_index;
MemArena *sf_arena;
mr_handle->layers_tot = (uint)BLI_listbase_count(&mask->masklayers);
mr_handle->layers = MEM_mallocN(sizeof(MaskRasterLayer) * mr_handle->layers_tot,
"MaskRasterLayer");
BLI_rctf_init_minmax(&mr_handle->bounds);
sf_arena = BLI_memarena_new(BLI_SCANFILL_ARENA_SIZE, __func__);
for (masklay = mask->masklayers.first, masklay_index = 0; masklay;
masklay = masklay->next, masklay_index++) {
/* we need to store vertex ranges for open splines for filling */
uint tot_splines;
MaskRasterSplineInfo *open_spline_ranges;
uint open_spline_index = 0;
MaskSpline *spline;
/* scanfill */
ScanFillContext sf_ctx;
ScanFillVert *sf_vert = NULL;
ScanFillVert *sf_vert_next = NULL;
ScanFillFace *sf_tri;
uint sf_vert_tot = 0;
uint tot_feather_quads = 0;
#ifdef USE_SCANFILL_EDGE_WORKAROUND
uint tot_boundary_used = 0;
uint tot_boundary_found = 0;
#endif
if (masklay->visibility_flag & MASK_HIDE_RENDER) {
/* skip the layer */
mr_handle->layers_tot--;
masklay_index--;
continue;
}
tot_splines = (uint)BLI_listbase_count(&masklay->splines);
open_spline_ranges = MEM_callocN(sizeof(*open_spline_ranges) * tot_splines, __func__);
BLI_scanfill_begin_arena(&sf_ctx, sf_arena);
for (spline = masklay->splines.first; spline; spline = spline->next) {
const bool is_cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0;
const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0;
float(*diff_points)[2];
uint tot_diff_point;
float(*diff_feather_points)[2];
float(*diff_feather_points_flip)[2];
uint tot_diff_feather_points;
const uint resol_a = BKE_mask_spline_resolution(spline, width, height) / 4;
const uint resol_b = BKE_mask_spline_feather_resolution(spline, width, height) / 4;
const uint resol = CLAMPIS(MAX2(resol_a, resol_b), 4, 512);
diff_points = BKE_mask_spline_differentiate_with_resolution(spline, resol, &tot_diff_point);
if (do_feather) {
diff_feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution(
spline, resol, false, &tot_diff_feather_points);
BLI_assert(diff_feather_points);
}
else {
tot_diff_feather_points = 0;
diff_feather_points = NULL;
}
if (tot_diff_point > 3) {
ScanFillVert *sf_vert_prev;
uint j;
sf_ctx.poly_nr++;
if (do_aspect_correct) {
if (width != height) {
float *fp;
float *ffp;
float asp;
if (width < height) {
fp = &diff_points[0][0];
ffp = tot_diff_feather_points ? &diff_feather_points[0][0] : NULL;
asp = (float)width / (float)height;
}
else {
fp = &diff_points[0][1];
ffp = tot_diff_feather_points ? &diff_feather_points[0][1] : NULL;
asp = (float)height / (float)width;
}
for (uint i = 0; i < tot_diff_point; i++, fp += 2) {
(*fp) = (((*fp) - 0.5f) / asp) + 0.5f;
}
if (tot_diff_feather_points) {
for (uint i = 0; i < tot_diff_feather_points; i++, ffp += 2) {
(*ffp) = (((*ffp) - 0.5f) / asp) + 0.5f;
}
}
}
}
/* fake aa, using small feather */
if (do_mask_aa == true) {
if (do_feather == false) {
tot_diff_feather_points = tot_diff_point;
diff_feather_points = MEM_mallocN(
sizeof(*diff_feather_points) * (size_t)tot_diff_feather_points, __func__);
/* add single pixel feather */
maskrasterize_spline_differentiate_point_outset(
diff_feather_points, diff_points, tot_diff_point, pixel_size, false);
}
else {
/* ensure single pixel feather, on any zero feather areas */
maskrasterize_spline_differentiate_point_outset(
diff_feather_points, diff_points, tot_diff_point, pixel_size, true);
}
}
if (is_fill) {
/* Apply intersections depending on fill settings. */
if (spline->flag & MASK_SPLINE_NOINTERSECT) {
BKE_mask_spline_feather_collapse_inner_loops(
spline, diff_feather_points, tot_diff_feather_points);
}
sf_vert_prev = scanfill_vert_add_v2_with_depth(&sf_ctx, diff_points[0], 0.0f);
sf_vert_prev->tmp.u = sf_vert_tot;
/* Absolute index of feather vert. */
sf_vert_prev->keyindex = sf_vert_tot + tot_diff_point;
sf_vert_tot++;
for (j = 1; j < tot_diff_point; j++) {
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, diff_points[j], 0.0f);
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = sf_vert_tot + tot_diff_point; /* absolute index of feather vert */
sf_vert_tot++;
}
sf_vert = sf_vert_prev;
sf_vert_prev = sf_ctx.fillvertbase.last;
for (j = 0; j < tot_diff_point; j++) {
ScanFillEdge *sf_edge = BLI_scanfill_edge_add(&sf_ctx, sf_vert_prev, sf_vert);
#ifdef USE_SCANFILL_EDGE_WORKAROUND
if (diff_feather_points) {
sf_edge->tmp.c = SF_EDGE_IS_BOUNDARY;
tot_boundary_used++;
}
#else
(void)sf_edge;
#endif
sf_vert_prev = sf_vert;
sf_vert = sf_vert->next;
}
if (diff_feather_points) {
BLI_assert(tot_diff_feather_points == tot_diff_point);
/* NOTE: only added for convenience, we don't in fact use these to scan-fill,
* only to create feather faces after scan-fill. */
for (j = 0; j < tot_diff_feather_points; j++) {
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, diff_feather_points[j], 1.0f);
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
}
tot_feather_quads += tot_diff_point;
}
}
else {
/* unfilled spline */
if (diff_feather_points) {
if (spline->flag & MASK_SPLINE_NOINTERSECT) {
diff_feather_points_flip = MEM_mallocN(sizeof(float[2]) * tot_diff_feather_points,
"diff_feather_points_flip");
float co_diff[2];
for (j = 0; j < tot_diff_point; j++) {
sub_v2_v2v2(co_diff, diff_points[j], diff_feather_points[j]);
add_v2_v2v2(diff_feather_points_flip[j], diff_points[j], co_diff);
}
BKE_mask_spline_feather_collapse_inner_loops(
spline, diff_feather_points, tot_diff_feather_points);
BKE_mask_spline_feather_collapse_inner_loops(
spline, diff_feather_points_flip, tot_diff_feather_points);
}
else {
diff_feather_points_flip = NULL;
}
open_spline_ranges[open_spline_index].vertex_offset = sf_vert_tot;
open_spline_ranges[open_spline_index].vertex_total = tot_diff_point;
/* TODO: an alternate functions so we can avoid double vector copy! */
for (j = 0; j < tot_diff_point; j++) {
/* center vert */
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, diff_points[j], 0.0f);
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
/* feather vert A */
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, diff_feather_points[j], 1.0f);
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
/* feather vert B */
if (diff_feather_points_flip) {
sf_vert = scanfill_vert_add_v2_with_depth(
&sf_ctx, diff_feather_points_flip[j], 1.0f);
}
else {
float co_diff[2];
sub_v2_v2v2(co_diff, diff_points[j], diff_feather_points[j]);
add_v2_v2v2(co_diff, diff_points[j], co_diff);
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, co_diff, 1.0f);
}
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
tot_feather_quads += 2;
}
if (!is_cyclic) {
tot_feather_quads -= 2;
}
if (diff_feather_points_flip) {
MEM_freeN(diff_feather_points_flip);
diff_feather_points_flip = NULL;
}
/* cap ends */
/* dummy init value */
open_spline_ranges[open_spline_index].vertex_total_cap_head = 0;
open_spline_ranges[open_spline_index].vertex_total_cap_tail = 0;
if (!is_cyclic) {
const float *fp_cent;
const float *fp_turn;
uint k;
fp_cent = diff_points[0];
fp_turn = diff_feather_points[0];
#define CALC_CAP_RESOL \
clampis_uint((uint)(len_v2v2(fp_cent, fp_turn) / (pixel_size * SPLINE_RESOL_CAP_PER_PIXEL)), \
SPLINE_RESOL_CAP_MIN, \
SPLINE_RESOL_CAP_MAX)
{
const uint vertex_total_cap = CALC_CAP_RESOL;
for (k = 1; k < vertex_total_cap; k++) {
const float angle = (float)k * (1.0f / (float)vertex_total_cap) * (float)M_PI;
float co_feather[2];
rotate_point_v2(co_feather, fp_turn, fp_cent, angle, asp_xy);
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, co_feather, 1.0f);
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
}
tot_feather_quads += vertex_total_cap;
open_spline_ranges[open_spline_index].vertex_total_cap_head = vertex_total_cap;
}
fp_cent = diff_points[tot_diff_point - 1];
fp_turn = diff_feather_points[tot_diff_point - 1];
{
const uint vertex_total_cap = CALC_CAP_RESOL;
for (k = 1; k < vertex_total_cap; k++) {
const float angle = (float)k * (1.0f / (float)vertex_total_cap) * (float)M_PI;
float co_feather[2];
rotate_point_v2(co_feather, fp_turn, fp_cent, -angle, asp_xy);
sf_vert = scanfill_vert_add_v2_with_depth(&sf_ctx, co_feather, 1.0f);
sf_vert->tmp.u = sf_vert_tot;
sf_vert->keyindex = SF_KEYINDEX_TEMP_ID;
sf_vert_tot++;
}
tot_feather_quads += vertex_total_cap;
open_spline_ranges[open_spline_index].vertex_total_cap_tail = vertex_total_cap;
}
}
open_spline_ranges[open_spline_index].is_cyclic = is_cyclic;
open_spline_index++;
#undef CALC_CAP_RESOL
/* end capping */
}
}
}
if (diff_points) {
MEM_freeN(diff_points);
}
if (diff_feather_points) {
MEM_freeN(diff_feather_points);
}
}
{
uint(*face_array)[4], *face; /* access coords */
float(*face_coords)[3], *cos; /* xy, z 0-1 (1.0 == filled) */
uint sf_tri_tot;
rctf bounds;
uint face_index;
int scanfill_flag = 0;
bool is_isect = false;
ListBase isect_remvertbase = {NULL, NULL};
ListBase isect_remedgebase = {NULL, NULL};
/* now we have all the splines */
face_coords = MEM_mallocN(sizeof(float[3]) * sf_vert_tot, "maskrast_face_coords");
/* init bounds */
BLI_rctf_init_minmax(&bounds);
/* coords */
cos = (float *)face_coords;
for (sf_vert = sf_ctx.fillvertbase.first; sf_vert; sf_vert = sf_vert_next) {
sf_vert_next = sf_vert->next;
copy_v3_v3(cos, sf_vert->co);
/* remove so as not to interfere with fill (called after) */
if (sf_vert->keyindex == SF_KEYINDEX_TEMP_ID) {
BLI_remlink(&sf_ctx.fillvertbase, sf_vert);
}
/* bounds */
BLI_rctf_do_minmax_v(&bounds, cos);
cos += 3;
}
/* --- inefficient self-intersect case --- */
/* if self intersections are found, its too tricky to attempt to map vertices
* so just realloc and add entirely new vertices - the result of the self-intersect check.
*/
if ((masklay->flag & MASK_LAYERFLAG_FILL_OVERLAP) &&
(is_isect = BLI_scanfill_calc_self_isect(
&sf_ctx, &isect_remvertbase, &isect_remedgebase))) {
uint sf_vert_tot_isect = (uint)BLI_listbase_count(&sf_ctx.fillvertbase);
uint i = sf_vert_tot;
face_coords = MEM_reallocN(face_coords,
sizeof(float[3]) * (sf_vert_tot + sf_vert_tot_isect));
cos = (float *)&face_coords[sf_vert_tot][0];
for (sf_vert = sf_ctx.fillvertbase.first; sf_vert; sf_vert = sf_vert->next) {
copy_v3_v3(cos, sf_vert->co);
sf_vert->tmp.u = i++;
cos += 3;
}
sf_vert_tot += sf_vert_tot_isect;
/* we need to calc polys after self intersect */
scanfill_flag |= BLI_SCANFILL_CALC_POLYS;
}
/* --- end inefficient code --- */
/* main scan-fill */
if ((masklay->flag & MASK_LAYERFLAG_FILL_DISCRETE) == 0) {
scanfill_flag |= BLI_SCANFILL_CALC_HOLES;
}
sf_tri_tot = (uint)BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, zvec);
if (is_isect) {
/* add removed data back, we only need edges for feather,
* but add verts back so they get freed along with others */
BLI_movelisttolist(&sf_ctx.fillvertbase, &isect_remvertbase);
BLI_movelisttolist(&sf_ctx.filledgebase, &isect_remedgebase);
}
face_array = MEM_mallocN(sizeof(*face_array) *
((size_t)sf_tri_tot + (size_t)tot_feather_quads),
"maskrast_face_index");
face_index = 0;
/* faces */
face = (uint *)face_array;
for (sf_tri = sf_ctx.fillfacebase.first; sf_tri; sf_tri = sf_tri->next) {
*(face++) = sf_tri->v3->tmp.u;
*(face++) = sf_tri->v2->tmp.u;
*(face++) = sf_tri->v1->tmp.u;
*(face++) = TRI_VERT;
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
/* start of feather faces... if we have this set,
* 'face_index' is kept from loop above */
BLI_assert(face_index == sf_tri_tot);
UNUSED_VARS_NDEBUG(face_index);
if (tot_feather_quads) {
ScanFillEdge *sf_edge;
for (sf_edge = sf_ctx.filledgebase.first; sf_edge; sf_edge = sf_edge->next) {
if (sf_edge->tmp.c == SF_EDGE_IS_BOUNDARY) {
*(face++) = sf_edge->v1->tmp.u;
*(face++) = sf_edge->v2->tmp.u;
*(face++) = sf_edge->v2->keyindex;
*(face++) = sf_edge->v1->keyindex;
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
#ifdef USE_SCANFILL_EDGE_WORKAROUND
tot_boundary_found++;
#endif
}
}
}
#ifdef USE_SCANFILL_EDGE_WORKAROUND
if (tot_boundary_found != tot_boundary_used) {
BLI_assert(tot_boundary_found < tot_boundary_used);
}
#endif
/* feather only splines */
while (open_spline_index > 0) {
const uint vertex_offset = open_spline_ranges[--open_spline_index].vertex_offset;
uint vertex_total = open_spline_ranges[open_spline_index].vertex_total;
uint vertex_total_cap_head = open_spline_ranges[open_spline_index].vertex_total_cap_head;
uint vertex_total_cap_tail = open_spline_ranges[open_spline_index].vertex_total_cap_tail;
uint k, j;
j = vertex_offset;
/* subtract one since we reference next vertex triple */
for (k = 0; k < vertex_total - 1; k++, j += 3) {
BLI_assert(j == vertex_offset + (k * 3));
*(face++) = j + 3; /* next span */ /* z 1 */
*(face++) = j + 0; /* z 1 */
*(face++) = j + 1; /* z 0 */
*(face++) = j + 4; /* next span */ /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
*(face++) = j + 0; /* z 1 */
*(face++) = j + 3; /* next span */ /* z 1 */
*(face++) = j + 5; /* next span */ /* z 0 */
*(face++) = j + 2; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
if (open_spline_ranges[open_spline_index].is_cyclic) {
*(face++) = vertex_offset + 0; /* next span */ /* z 1 */
*(face++) = j + 0; /* z 1 */
*(face++) = j + 1; /* z 0 */
*(face++) = vertex_offset + 1; /* next span */ /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
*(face++) = j + 0; /* z 1 */
*(face++) = vertex_offset + 0; /* next span */ /* z 1 */
*(face++) = vertex_offset + 2; /* next span */ /* z 0 */
*(face++) = j + 2; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
else {
uint midvidx = vertex_offset;
/***************
* cap end 'a' */
j = midvidx + (vertex_total * 3);
for (k = 0; k < vertex_total_cap_head - 2; k++, j++) {
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = j + 0; /* z 0 */
*(face++) = j + 1; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
j = vertex_offset + (vertex_total * 3);
/* 2 tris that join the original */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 1; /* z 0 */
*(face++) = j + 0; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = j + vertex_total_cap_head - 2; /* z 0 */
*(face++) = midvidx + 2; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
/***************
* cap end 'b' */
/* ... same as previous but v 2-3 flipped, and different initial offsets */
j = vertex_offset + (vertex_total * 3) + (vertex_total_cap_head - 1);
midvidx = vertex_offset + (vertex_total * 3) - 3;
for (k = 0; k < vertex_total_cap_tail - 2; k++, j++) {
*(face++) = midvidx; /* z 1 */
*(face++) = midvidx; /* z 1 */
*(face++) = j + 1; /* z 0 */
*(face++) = j + 0; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
j = vertex_offset + (vertex_total * 3) + (vertex_total_cap_head - 1);
/* 2 tris that join the original */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = j + 0; /* z 0 */
*(face++) = midvidx + 1; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 0; /* z 1 */
*(face++) = midvidx + 2; /* z 0 */
*(face++) = j + vertex_total_cap_tail - 2; /* z 0 */
face_index++;
FACE_ASSERT(face - 4, sf_vert_tot);
}
}
MEM_freeN(open_spline_ranges);
#if 0
fprintf(stderr,
"%u %u (%u %u), %u\n",
face_index,
sf_tri_tot + tot_feather_quads,
sf_tri_tot,
tot_feather_quads,
tot_boundary_used - tot_boundary_found);
#endif
#ifdef USE_SCANFILL_EDGE_WORKAROUND
BLI_assert(face_index + (tot_boundary_used - tot_boundary_found) ==
sf_tri_tot + tot_feather_quads);
#else
BLI_assert(face_index == sf_tri_tot + tot_feather_quads);
#endif
{
MaskRasterLayer *layer = &mr_handle->layers[masklay_index];
if (BLI_rctf_isect(&default_bounds, &bounds, &bounds)) {
#ifdef USE_SCANFILL_EDGE_WORKAROUND
layer->face_tot = (sf_tri_tot + tot_feather_quads) -
(tot_boundary_used - tot_boundary_found);
#else
layer->face_tot = (sf_tri_tot + tot_feather_quads);
#endif
layer->face_coords = face_coords;
layer->face_array = face_array;
layer->bounds = bounds;
layer_bucket_init(layer, pixel_size);
BLI_rctf_union(&mr_handle->bounds, &bounds);
}
else {
MEM_freeN(face_coords);
MEM_freeN(face_array);
layer_bucket_init_dummy(layer);
}
/* copy as-is */
layer->alpha = masklay->alpha;
layer->blend = masklay->blend;
layer->blend_flag = masklay->blend_flag;
layer->falloff = masklay->falloff;
}
// printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads);
}
/* Add triangles. */
BLI_scanfill_end_arena(&sf_ctx, sf_arena);
}
BLI_memarena_free(sf_arena);
}
/* --------------------------------------------------------------------- */
/* functions that run inside the sampling thread (keep fast!) */
/* --------------------------------------------------------------------- */
/* 2D ray test */
#if 0
static float maskrasterize_layer_z_depth_tri(const float pt[2],
const float v1[3],
const float v2[3],
const float v3[3])
{
float w[3];
barycentric_weights_v2(v1, v2, v3, pt, w);
return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]);
}
#endif
static float maskrasterize_layer_z_depth_quad(
const float pt[2], const float v1[3], const float v2[3], const float v3[3], const float v4[3])
{
float w[4];
barycentric_weights_v2_quad(v1, v2, v3, v4, pt, w);
// return (v1[2] * w[0]) + (v2[2] * w[1]) + (v3[2] * w[2]) + (v4[2] * w[3]);
return w[2] + w[3]; /* we can make this assumption for small speedup */
}
static float maskrasterize_layer_isect(const uint *face,
float (*cos)[3],
const float dist_orig,
const float xy[2])
{
/* we always cast from same place only need xy */
if (face[3] == TRI_VERT) {
/* --- tri --- */
#if 0
/* not essential but avoids unneeded extra lookups */
if ((cos[0][2] < dist_orig) || (cos[1][2] < dist_orig) || (cos[2][2] < dist_orig)) {
if (isect_point_tri_v2_cw(xy, cos[face[0]], cos[face[1]], cos[face[2]])) {
/* we know all tris are close for now */
return maskrasterize_layer_z_depth_tri(xy, cos[face[0]], cos[face[1]], cos[face[2]]);
}
}
#else
/* we know all tris are close for now */
if (isect_point_tri_v2_cw(xy, cos[face[0]], cos[face[1]], cos[face[2]])) {
return 0.0f;
}
#endif
}
else {
/* --- quad --- */
/* not essential but avoids unneeded extra lookups */
if ((cos[0][2] < dist_orig) || (cos[1][2] < dist_orig) || (cos[2][2] < dist_orig) ||
(cos[3][2] < dist_orig)) {
/* needs work */
#if 1
/* quad check fails for bow-tie, so keep using 2 tri checks */
// if (isect_point_quad_v2(xy, cos[face[0]], cos[face[1]], cos[face[2]], cos[face[3]]))
if (isect_point_tri_v2(xy, cos[face[0]], cos[face[1]], cos[face[2]]) ||
isect_point_tri_v2(xy, cos[face[0]], cos[face[2]], cos[face[3]])) {
return maskrasterize_layer_z_depth_quad(
xy, cos[face[0]], cos[face[1]], cos[face[2]], cos[face[3]]);
}
#elif 1
/* don't use isect_point_tri_v2_cw because we could have bow-tie quads */
if (isect_point_tri_v2(xy, cos[face[0]], cos[face[1]], cos[face[2]])) {
return maskrasterize_layer_z_depth_tri(xy, cos[face[0]], cos[face[1]], cos[face[2]]);
}
else if (isect_point_tri_v2(xy, cos[face[0]], cos[face[2]], cos[face[3]])) {
return maskrasterize_layer_z_depth_tri(xy, cos[face[0]], cos[face[2]], cos[face[3]]);
}
#else
/* cheat - we know first 2 verts are z0.0f and second 2 are z 1.0f */
/* ... worth looking into */
#endif
}
}
return 1.0f;
}
BLI_INLINE uint layer_bucket_index_from_xy(MaskRasterLayer *layer, const float xy[2])
{
BLI_assert(BLI_rctf_isect_pt_v(&layer->bounds, xy));
return (uint)((xy[0] - layer->bounds.xmin) * layer->buckets_xy_scalar[0]) +
((uint)((xy[1] - layer->bounds.ymin) * layer->buckets_xy_scalar[1]) * layer->buckets_x);
}
static float layer_bucket_depth_from_xy(MaskRasterLayer *layer, const float xy[2])
{
uint index = layer_bucket_index_from_xy(layer, xy);
uint *face_index = layer->buckets_face[index];
if (face_index) {
uint(*face_array)[4] = layer->face_array;
float(*cos)[3] = layer->face_coords;
float best_dist = 1.0f;
while (*face_index != TRI_TERMINATOR_ID) {
const float test_dist = maskrasterize_layer_isect(
face_array[*face_index], cos, best_dist, xy);
if (test_dist < best_dist) {
best_dist = test_dist;
/* comparing with 0.0f is OK here because triangles are always zero depth */
if (best_dist == 0.0f) {
/* bail early, we're as close as possible */
return 0.0f;
}
}
face_index++;
}
return best_dist;
}
return 1.0f;
}
float BKE_maskrasterize_handle_sample(MaskRasterHandle *mr_handle, const float xy[2])
{
/* can't do this because some layers may invert */
/* if (BLI_rctf_isect_pt_v(&mr_handle->bounds, xy)) */
const uint layers_tot = mr_handle->layers_tot;
MaskRasterLayer *layer = mr_handle->layers;
/* return value */
float value = 0.0f;
for (uint i = 0; i < layers_tot; i++, layer++) {
float value_layer;
/* also used as signal for unused layer (when render is disabled) */
if (layer->alpha != 0.0f && BLI_rctf_isect_pt_v(&layer->bounds, xy)) {
value_layer = 1.0f - layer_bucket_depth_from_xy(layer, xy);
switch (layer->falloff) {
case PROP_SMOOTH:
/* ease - gives less hard lines for dilate/erode feather */
value_layer = (3.0f * value_layer * value_layer -
2.0f * value_layer * value_layer * value_layer);
break;
case PROP_SPHERE:
value_layer = sqrtf(2.0f * value_layer - value_layer * value_layer);
break;
case PROP_ROOT:
value_layer = sqrtf(value_layer);
break;
case PROP_SHARP:
value_layer = value_layer * value_layer;
break;
case PROP_INVSQUARE:
value_layer = value_layer * (2.0f - value_layer);
break;
case PROP_LIN:
default:
/* nothing */
break;
}
if (layer->blend != MASK_BLEND_REPLACE) {
value_layer *= layer->alpha;
}
}
else {
value_layer = 0.0f;
}
if (layer->blend_flag & MASK_BLENDFLAG_INVERT) {
value_layer = 1.0f - value_layer;
}
switch (layer->blend) {
case MASK_BLEND_MERGE_ADD:
value += value_layer * (1.0f - value);
break;
case MASK_BLEND_MERGE_SUBTRACT:
value -= value_layer * value;
break;
case MASK_BLEND_ADD:
value += value_layer;
break;
case MASK_BLEND_SUBTRACT:
value -= value_layer;
break;
case MASK_BLEND_LIGHTEN:
value = max_ff(value, value_layer);
break;
case MASK_BLEND_DARKEN:
value = min_ff(value, value_layer);
break;
case MASK_BLEND_MUL:
value *= value_layer;
break;
case MASK_BLEND_REPLACE:
value = (value * (1.0f - layer->alpha)) + (value_layer * layer->alpha);
break;
case MASK_BLEND_DIFFERENCE:
value = fabsf(value - value_layer);
break;
default: /* same as add */
CLOG_ERROR(&LOG, "unhandled blend type: %d", layer->blend);
BLI_assert(0);
value += value_layer;
break;
}
/* clamp after applying each layer so we don't get
* issues subtracting after accumulating over 1.0f */
CLAMP(value, 0.0f, 1.0f);
}
return value;
}
typedef struct MaskRasterizeBufferData {
MaskRasterHandle *mr_handle;
float x_inv, y_inv;
float x_px_ofs, y_px_ofs;
uint width;
float *buffer;
} MaskRasterizeBufferData;
static void maskrasterize_buffer_cb(void *__restrict userdata,
const int y,
const TaskParallelTLS *__restrict UNUSED(tls))
{
MaskRasterizeBufferData *data = userdata;
MaskRasterHandle *mr_handle = data->mr_handle;
float *buffer = data->buffer;
const uint width = data->width;
const float x_inv = data->x_inv;
const float x_px_ofs = data->x_px_ofs;
uint i = (uint)y * width;
float xy[2];
xy[1] = ((float)y * data->y_inv) + data->y_px_ofs;
for (uint x = 0; x < width; x++, i++) {
xy[0] = ((float)x * x_inv) + x_px_ofs;
buffer[i] = BKE_maskrasterize_handle_sample(mr_handle, xy);
}
}
void BKE_maskrasterize_buffer(MaskRasterHandle *mr_handle,
const uint width,
const uint height,
/* Cannot be const, because it is assigned to non-const variable.
* NOLINTNEXTLINE: readability-non-const-parameter. */
float *buffer)
{
const float x_inv = 1.0f / (float)width;
const float y_inv = 1.0f / (float)height;
MaskRasterizeBufferData data = {
.mr_handle = mr_handle,
.x_inv = x_inv,
.y_inv = y_inv,
.x_px_ofs = x_inv * 0.5f,
.y_px_ofs = y_inv * 0.5f,
.width = width,
.buffer = buffer,
};
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = ((size_t)height * width > 10000);
BLI_task_parallel_range(0, (int)height, &data, maskrasterize_buffer_cb, &settings);
}